Versions Compared

Key

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

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

Данный пример может использоваться как стример для публикации 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);
    if (mSendAudio.isChecked()) {    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);
        }localRender.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);
        if (mUseStereo.isChecked()) {localRender.setMirror(true);
            audioConstraints.setUseStereo(truelocalRender.requestLayout();

        }
   switchRenderLayout.setPosition(0, 0, 100, 100);
     if (!mDefaultPublishAudioBitrate.isChecked() && mDefaultPublishAudioBitratenewSurfaceRenderer.getTextsetZOrderMediaOverlay().length() > 0) {
true);
        newSurfaceRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);
       audioConstraints newSurfaceRenderer.setBitrate(Integer.parseInt(mPublishAudioBitrate.getText().toString()))setMirror(true);
        }
    }
    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);
}

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 denied by user");
    } else {
        Flashphoner.getLocalMediaAccess(getConstraints(), localRender);
        mTestButton.setText(R.string.action_release);
        mTestButton.setTag(R.string.action_release);
        mStartButton.setEnabled(false);
        soundMeter = new SoundMeter();
        soundMeter.start();
        soundMeter.getTimer().scheduleAtFixedRate(new TimerTask() {
           @Override
           public void run() {
               runOnUiThread(new Runnable() {
                   @Override
                   public void run() {
                       String text = "Level: " + Math.floor(soundMeter.getAmplitude() * 10);
                       mMicLevel.setText(text);
                   }
               });
           }
        }, 0, 300);
        Log.i(TAG, "Permission has been granted by user");
    }
break;

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

Flashphoner.createSession() код

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

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

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

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

AudioConstraints, VideoConstraints code

Code Block
languagejava
themeRDark
@NonNull
private Constraints getConstraints() {
    AudioConstraints audioConstraints = @Overridenull;
       public void run(if (mSendAudio.isChecked()) {
        audioConstraints = new mStartButton.setText(R.string.action_stopAudioConstraints();
        if   mStartButton.setTag(R.string.action_stop);
(mUseFEC.isChecked()) {
            mStartButtonaudioConstraints.setEnabledsetUseFEC(true);
           mTestButton.setEnabled(false);}
        if   mStatusView.setText(connectionmUseStereo.getStatusisChecked());; {

            /**audioConstraints.setUseStereo(true);
         }
    * The options for the stream to publish are set.
             * The stream name is passed when StreamOptions object is created.if (!mDefaultPublishAudioBitrate.isChecked() && mDefaultPublishAudioBitrate.getText().length() > 0) {
            audioConstraints.setBitrate(Integer.parseInt(mPublishAudioBitrate.getText().toString()));
        }
    }
 * VideoConstraints object isVideoConstraints usedvideoConstraints to= setnull;
 the source camera, FPS and resolution.if (mSendVideo.isChecked()) {
        videoConstraints = new VideoConstraints();
  * Stream constraints are set with method StreamOptions.setConstraints(). videoConstraints.setCameraId(((MediaDevice) mCameraSpinner.getSpinner().getSelectedItem()).getId());
        if (mCameraFPS.getText().length() > 0)  */{
           StreamOptions streamOptions = new StreamOptions(streamName videoConstraints.setVideoFps(Integer.parseInt(mCameraFPS.getText().toString()));
        }
   Constraints constraints = getConstraints();
  if (mWidth.getText().length() > 0      streamOptions.setConstraints(constraints);
  && mHeight.getText().length() > 0) {
         String[] stripCodec = {(String) mStripStreamerCodec.getSpinner videoConstraints.setResolution(Integer.parseInt(mWidth.getText().getSelectedItemtoString()};
),
                    streamOptions.setStripCodecs(stripCodecInteger.parseInt(mHeight.getText().toString()));

        }
   /**
     if (!mDefaultPublishVideoBitrate.isChecked() && mPublishVideoBitrate.getText().length() > 0) {
      *  Stream is created with method Session.createStream(). videoConstraints.setBitrate(Integer.parseInt(mPublishVideoBitrate.getText().toString()));
        }
     */}
    return new      publishStream = session.createStream(streamOptions);
  Constraints(audioConstraints, videoConstraints);
}

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

Flashphoner.getLocalMediaAccess() code

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

  • настройки аудио и видео, заданные пользователем
  • локальный объект SurfaceViewRenderer localRenderer для вывода изображения с выбранной камеры
Code Block
languagejava
themeRDark
case TEST_REQUEST_CODE: {
    if (grantResults.length ==   ...0 ||
       }
   });
}

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,
   grantResults[0] != PackageManager.PERMISSION_GRANTED ||
          grantResults[1] != PackageManager.PERMISSION_GRANTED) {
      new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA},
         PUBLISH_REQUEST_CODE);

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

Stream.publish() код

Code Block
languagejs
themeRDark
case PUBLISH_REQUEST_CODE: Log.i(TAG, "Permission has been denied by user");
    } else {
    if (grantResults.length == 0 ||
    Flashphoner.getLocalMediaAccess(getConstraints(), localRender);
        mTestButton.setText(R.string.action_release);
      grantResults[0] != PackageManager.PERMISSION_GRANTED || mTestButton.setTag(R.string.action_release);
        mStartButton.setEnabled(false);
   grantResults[1] != PackageManager.PERMISSION_GRANTED) {
  soundMeter =     mStartButton.setEnabled(falsenew SoundMeter();
        mTestButtonsoundMeter.setEnabledstart(false);
        session.disconnect();...
        Log.i(TAG, "Permission has been deniedgranted by user");
    } else {
        /**
          * Method Stream.publish() is called to publish stream.
          */
        publishStream.publish();
        Log.i(TAG, "Permission has been granted by user");
    }
    break;
}

...


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. Получение от сервера события, подтверждающего успешную публикацию потока

StreamStatusEvent PUBLISHING код

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

session.onConnected() code

Code Block
languagejsjava
themeRDark
publishStream.on(new StreamStatusEvent() {
    @Override
    public void onStreamStatusonConnected(final Stream stream, final StreamStatus streamStatusConnection connection) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                if (StreamStatus.PUBLISHING.equals(streamStatus)) {mStartButton.setText(R.string.action_stop);
                    /**
                      * The options for the stream to play are set.mStartButton.setTag(R.string.action_stop);
           mStartButton.setEnabled(true);
           mTestButton.setEnabled(false);
            * The stream name is passed when StreamOptions object is createdmStatusView.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,
       StreamOptions streamOptions = new StreamOptions(streamName);
new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA},
         PUBLISH_REQUEST_CODE);

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

Stream.publish() code

Code Block
languagejava
themeRDark
case PUBLISH_REQUEST_CODE: {
         streamOptions.setConstraints(new Constraints(mReceiveAudio.isChecked(), mReceiveVideo.isChecked()));
if (grantResults.length == 0 ||
           grantResults[0] != PackageManager.PERMISSION_GRANTED ||
      VideoConstraints videoConstraints = null;
  grantResults[1] != PackageManager.PERMISSION_GRANTED) {
        mStartButton.setEnabled(false);
       if (mReceiveVideomTestButton.isCheckedsetEnabled(false)) {;
        session.disconnect();
        Log.i(TAG, "Permission has been denied    videoConstraints = new VideoConstraints(by user");
    } else {
        /**
          if (!mDefaultPlayResolution.isChecked() && mPlayWidth.getText().length() > 0 && mPlayHeight.getText().length() > 0) {* Method Stream.publish() is called to publish stream.
          */
        publishStream.publish();
        Log.i(TAG, "Permission has been granted by user");
    }
    videoConstraints.setResolution(Integer.parseInt(mPlayWidth.getText().toString()),break;
}

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

StreamStatusEvent PUBLISHING code

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

Code Block
languagejava
themeRDark
publishStream.on(new StreamStatusEvent() {
    @Override
    public void onStreamStatus(final Stream stream, final StreamStatus streamStatus) {
            Integer.parseInt(mPlayHeight.getText().toString()));runOnUiThread(new Runnable() {
            @Override
            }
        public void run() {
                if (!mDefaultPlayBitrate.isChecked() && mPlayBitrate.getText().length() > 0StreamStatus.PUBLISHING.equals(streamStatus)) {
                    /**
        videoConstraints.setBitrate(Integer.parseInt(mPlayBitrate.getText().toString()));
              * The options for the stream to play are  }set.
                        if (!mDefaultPlayQuality.isChecked() && mPlayQuality.getText().length() > 0) {
        * The stream name is passed when StreamOptions object is created.
                      videoConstraints.setQuality(Integer.parseInt(mPlayQuality.getText().toString()));*/
                    StreamOptions streamOptions = new }StreamOptions(streamName);

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

                    AudioConstraintsVideoConstraints audioConstraintsvideoConstraints = null;
                    if (mReceiveAudiomReceiveVideo.isChecked()) {
                        audioConstraintsvideoConstraints = new AudioConstraintsVideoConstraints();
                        ...
                    }
                    streamOptions.setConstraints(new Constraints(audioConstraints, videoConstraints))AudioConstraints audioConstraints = null;
                    String[] stripCodec = {(String) mStripPlayerCodec.getSpinner().getSelectedItem()};if (mReceiveAudio.isChecked()) {
                    streamOptions.setStripCodecs(stripCodec);
    audioConstraints = new AudioConstraints();
             /**
       }
               * Stream is created with method Session.createStream().streamOptions.setConstraints(new Constraints(audioConstraints, videoConstraints));
                    String[] stripCodec */
  = {(String) mStripPlayerCodec.getSpinner().getSelectedItem()};
                  playStream = sessionstreamOptions.createStreamsetStripCodecs(streamOptionsstripCodec);

                    /**
                      * Callback function for stream status change Stream is addedcreated towith display the statusmethod Session.createStream().
                      */
                    playStream = session.on(new StreamStatusEvent() {createStream(streamOptions);
                        @Override...
                    /**
    public void onStreamStatus(final Stream stream, final StreamStatus streamStatus) {
          * Method Stream.play() is called to start playback of the stream.
        runOnUiThread(new Runnable() {
            */
                    @OverrideplayStream.play();
                    if (mSendVideo.isChecked())
           public void run() {
          mSwitchCameraButton.setEnabled(true);
                          if (!StreamStatus.PLAYING.equals(streamStatus)) {mSwitchRendererButton.setEnabled(true);
                   } else {
                   Log.e(TAG, "Can not playpublish stream " + stream.getName() + " " + streamStatus);
               }
               mStatusView.setText(streamStatus.toString());
           }
       });
    }
});

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

Stream.switchCamera() code

Code Block
languagejava
themeRDark
mSwitchCameraButton.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View view)   }{
        if (publishStream != null) {
                        mStatusView.setText(streamStatus.toString())mSwitchCameraButton.setEnabled(false);
            publishStream.switchCamera(new CameraSwitchHandler() {
                  }@Override
                  public void onCameraSwitchDone(boolean var1) {
      });
              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
        mSwitchCameraButton.setEnabled(true);
        public void onCameraSwitchError(String var1) {
        mSwitchRendererButton.setEnabled(true);
               } else runOnUiThread(new Runnable() {
                   Log.e(TAG, "Can not publish stream "@Override
 + stream.getName() + " " + streamStatus);
               }
  public void run() {
          mStatusView.setText(streamStatus.toString());
           }
       }mSwitchCameraButton.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() {}
            });
        @Override}
    }

});

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

Stream.switchRenderer() code

Code Block
languagejava
themeRDark
mSwitchRendererButton.setOnClickListener(new OnClickListener() {
      @Override
    public void onCameraSwitchDoneonClick(booleanView var1v) {
                    runOnUiThread(new Runnableif (spinner.getSelectedItemId() == 0){
            if (isSwitchRemoteRenderer) {
          @Override
      playStream.switchRenderer(remoteRender);
                isSwitchRemoteRenderer  public void run() {= 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) {
                mSwitchCameraButtonplayStream.setEnabledswitchRenderer(truenewSurfaceRenderer);
                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 voidboolean onClick(View vonKeyDown(int keyCode, KeyEvent event) {
        int if (spinner.getSelectedItemIdcurrentVolume = Flashphoner.getVolume();
 == 0){
       switch (keyCode) {
     if (isSwitchRemoteRenderer) {
     case KeyEvent.KEYCODE_VOLUME_DOWN:
          playStream.switchRenderer(remoteRender);
      if (currentVolume == 1) {
      isSwitchRemoteRenderer = false;
            }
Flashphoner.setVolume(0);
              if (!isSwitchLocalRenderer) {}
                publishStreammPlayVolume.switchRenderersetProgress(newSurfaceRenderercurrentVolume-1);
                isSwitchLocalRenderer = truebreak;
            } else {case KeyEvent.KEYCODE_VOLUME_UP:
                publishStream.switchRenderer(localRender);
   if (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);
      }
   });
}