...
При инициализации методу init() передается объект Сontext.
Code Block | ||||
---|---|---|---|---|
| ||||
Flashphoner.init(this); |
2. Получение списка доступных медиа-устройств.
Flashphoner.getMediaDevices(), MediaDeviceList.getAudioList(), MediaDeviceList.getVideoList() код
Code Block | ||||
---|---|---|---|---|
| ||||
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()); |
...
AudioConstraints, VideoConstraints код
Code Block | ||||
---|---|---|---|---|
| ||||
@NonNull private Constraints getConstraints() { AudioConstraints audioConstraints = null; if (mSendAudio.isChecked()) { audioConstraints = new AudioConstraints(); if (mUseFEC.isChecked()) { audioConstraints.setUseFEC(true); } if (mUseStereo.isChecked()) { audioConstraints.setUseStereo 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 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( Integer.parseInt(mHeight.getText().toString())); } if (!mDefaultPublishVideoBitrate.isChecked() && mPublishVideoBitrate.getText().length() > 0) { videoConstraints.setBitrate(Integer.parseInt(mPublishVideoBitrate.getText().toString())); } } return new Constraints(audioConstraints, videoConstraints); } |
...
- настройки аудио и видео, заданные пользователем
- локальный объект SurfaceViewRenderer localRenderer для вывода изображения с выбранной камеры
Code Block | ||||
---|---|---|---|---|
| ||||
case TEST_REQUEST_CODE: { if 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 soundMeter = new SoundMeter(); soundMeter.start(); soundMeter.getTimer().scheduleAtFixedRate(new TimerTask() { @Override public void run() { runOnUiThread(new Runnable() { @Override public void run() { @Override public void run() { String text = "Level: " + Math.floor(soundMeter.getAmplitude() * 10); mMicLevel 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 | ||
---|---|---|
| ||
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 | ||
---|---|---|
| ||
session.connect(new Connection()); |
7. Получение от сервера события, подтверждающего успешное соединение.
session.onConnected() код
Code Block | ||
---|---|---|
| ||
@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());;
/**
* The options for the stream to publish are set.
* The stream name is passed when StreamOptions object is created.
* VideoConstraints object is used to set the source camera, FPS and resolution.
* Stream constraints are set with method StreamOptions.setConstraints().
*/
StreamOptions streamOptions = new StreamOptions(streamName);
Constraints constraints = getConstraints();
streamOptions.setConstraints(constraints);
String[] stripCodec = {(String) mStripStreamerCodec.getSpinner().getSelectedItem()};
streamOptions.setStripCodecs(stripCodec);
/**
* Stream is created with method Session.createStream().
*/
publishStream = session.createStream(streamOptions);
...
}
});
} |
8. Создание потока и подготовка к публикации
session.createStream() код
Code Block | ||
---|---|---|
| ||
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); |
9. Публикация потока
Stream.publish() код
Code Block | ||
---|---|---|
| ||
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;
} |
10. Получение от сервера события, подтверждающего успешную публикацию потока
StreamStatusEvent PUBLISHING код
При получении данного события создается превью-видеопоток при помощи Session.createStream() и вызывается Stream.play() для его воспроизведения.
Code Block | ||
---|---|---|
| ||
publishStream.on(new StreamStatusEvent() {
@Override
public void onStreamStatus(final Stream stream, final StreamStatus streamStatus) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (StreamStatus.PUBLISHING.equals(streamStatus)) {
/**
* The options for the stream to play are set.
* The stream name is passed when StreamOptions object is created.
*/
StreamOptions streamOptions = new StreamOptions(streamName);
streamOptions.setConstraints(new Constraints(mReceiveAudio.isChecked(), mReceiveVideo.isChecked()));
VideoConstraints videoConstraints = null;
if (mReceiveVideo.isChecked()) {
videoConstraints = new VideoConstraints();
if (!mDefaultPlayResolution.isChecked() && mPlayWidth.getText().length() > 0 && mPlayHeight.getText().length() > 0) {
videoConstraints.setResolution(Integer.parseInt(mPlayWidth.getText().toString()),
Integer.parseInt(mPlayHeight.getText().toString()));
}
if (!mDefaultPlayBitrate.isChecked() && mPlayBitrate.getText().length() > 0) {
videoConstraints.setBitrate(Integer.parseInt(mPlayBitrate.getText().toString()));
}
if (!mDefaultPlayQuality.isChecked() && mPlayQuality.getText().length() > 0) {
videoConstraints.setQuality(Integer.parseInt(mPlayQuality.getText().toString()));
}
}
AudioConstraints audioConstraints = null;
if (mReceiveAudio.isChecked()) {
audioConstraints = new AudioConstraints();
}
streamOptions.setConstraints(new Constraints(audioConstraints, videoConstraints));
String[] stripCodec = {(String) mStripPlayerCodec.getSpinner().getSelectedItem()};
streamOptions.setStripCodecs(stripCodec);
/**
* Stream is created with method Session.createStream().
*/
playStream = session.createStream(streamOptions);
/**
* Callback function for stream status change is added to display the status.
*/
playStream.on(new StreamStatusEvent() {
@Override
public void onStreamStatus(final Stream stream, final StreamStatus streamStatus) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (!StreamStatus.PLAYING.equals(streamStatus)) {
Log.e(TAG, "Can not play stream " + stream.getName() + " " + streamStatus);
}
mStatusView.setText(streamStatus.toString());
}
});
}
});
/**
* Method Stream.play() is called to start playback of the stream.
*/
playStream.play();
if (mSendVideo.isChecked())
mSwitchCameraButton.setEnabled(true);
mSwitchRendererButton.setEnabled(true);
} else {
Log.e(TAG, "Can not publish stream " + stream.getName() + " " + streamStatus);
}
mStatusView.setText(streamStatus.toString());
}
});
}
}); |
11. Переключение камеры во время трансляции
Stream.switchCamera() код
Code Block | ||
---|---|---|
| ||
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);
}
});
}
});
}
}
}); |
12. Переключение объекта для отображения видеопотока во время трансляции
Stream.switchRenderer() код
Code Block | ||
---|---|---|
| ||
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; } } } }); } }, 0, 300); Log.i(TAG, "Permission has been granted by user"); } break; |
5. Создание сессии
Flashphoner.createSession() код
Методу передается объект SessionOptions со следующими параметрами
- URL WCS-сервера
- SurfaceViewRenderer localRenderer, который будет использоваться для отображения видео с камеры
- SurfaceViewRenderer remoteRenderer, который будет использоваться для воспроизведения опубликованного видеопотока
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); |
6. Подключение к серверу.
Session.connect(). код
Code Block | ||
---|---|---|
| ||
session.connect(new Connection()); |
7. Получение от сервера события, подтверждающего успешное соединение.
session.onConnected() код
Code Block | ||||
---|---|---|---|---|
| ||||
@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());;
/**
* The options for the stream to publish are set.
* The stream name is passed when StreamOptions object is created.
* VideoConstraints object is used to set the source camera, FPS and resolution.
* Stream constraints are set with method StreamOptions.setConstraints().
*/
StreamOptions streamOptions = new StreamOptions(streamName);
Constraints constraints = getConstraints();
streamOptions.setConstraints(constraints);
String[] stripCodec = {(String) mStripStreamerCodec.getSpinner().getSelectedItem()};
streamOptions.setStripCodecs(stripCodec);
/**
* Stream is created with method Session.createStream().
*/
publishStream = session.createStream(streamOptions);
...
}
});
} |
8. Создание потока и подготовка к публикации
session.createStream() код
Code Block | ||||
---|---|---|---|---|
| ||||
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); |
9. Публикация потока
Stream.publish() код
Code Block | ||||
---|---|---|---|---|
| ||||
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;
} |
10. Получение от сервера события, подтверждающего успешную публикацию потока
StreamStatusEvent PUBLISHING код
При получении данного события создается превью-видеопоток при помощи Session.createStream() и вызывается Stream.play() для его воспроизведения.
Code Block | ||||
---|---|---|---|---|
| ||||
publishStream.on(new StreamStatusEvent() {
@Override
public void onStreamStatus(final Stream stream, final StreamStatus streamStatus) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (StreamStatus.PUBLISHING.equals(streamStatus)) {
/**
* The options for the stream to play are set.
* The stream name is passed when StreamOptions object is created.
*/
StreamOptions streamOptions = new StreamOptions(streamName);
streamOptions.setConstraints(new Constraints(mReceiveAudio.isChecked(), mReceiveVideo.isChecked()));
VideoConstraints videoConstraints = null;
if (mReceiveVideo.isChecked()) {
videoConstraints = new VideoConstraints();
if (!mDefaultPlayResolution.isChecked() && mPlayWidth.getText().length() > 0 && mPlayHeight.getText().length() > 0) {
videoConstraints.setResolution(Integer.parseInt(mPlayWidth.getText().toString()),
Integer.parseInt(mPlayHeight.getText().toString()));
}
if (!mDefaultPlayBitrate.isChecked() && mPlayBitrate.getText().length() > 0) {
videoConstraints.setBitrate(Integer.parseInt(mPlayBitrate.getText().toString()));
}
if (!mDefaultPlayQuality.isChecked() && mPlayQuality.getText().length() > 0) {
videoConstraints.setQuality(Integer.parseInt(mPlayQuality.getText().toString()));
}
}
AudioConstraints audioConstraints = null;
if (mReceiveAudio.isChecked()) {
audioConstraints = new AudioConstraints();
}
streamOptions.setConstraints(new Constraints(audioConstraints, videoConstraints));
String[] stripCodec = {(String) mStripPlayerCodec.getSpinner().getSelectedItem()};
streamOptions.setStripCodecs(stripCodec);
/**
* Stream is created with method Session.createStream().
*/
playStream = session.createStream(streamOptions);
/**
* Callback function for stream status change is added to display the status.
*/
playStream.on(new StreamStatusEvent() {
@Override
public void onStreamStatus(final Stream stream, final StreamStatus streamStatus) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (!StreamStatus.PLAYING.equals(streamStatus)) {
Log.e(TAG, "Can not play stream " + stream.getName() + " " + streamStatus);
}
mStatusView.setText(streamStatus.toString());
}
});
}
});
/**
* Method Stream.play() is called to start playback of the stream.
*/
playStream.play();
if (mSendVideo.isChecked())
mSwitchCameraButton.setEnabled(true);
mSwitchRendererButton.setEnabled(true);
} else {
Log.e(TAG, "Can not publish stream " + stream.getName() + " " + streamStatus);
}
mStatusView.setText(streamStatus.toString());
}
});
}
}); |
11. Переключение камеры во время трансляции
Stream.switchCamera() код
Code Block | ||||
---|---|---|---|---|
| ||||
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);
}
});
}
});
}
}
}); |
12. Переключение объекта для отображения видеопотока во время трансляции
Stream.switchRenderer() код
Code Block | ||||
---|---|---|---|---|
| ||||
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;
}
}
}
}); |
13. Закрытие соединения.
Session.disconnect() код
Code Block | ||||
---|---|---|---|---|
| ||||
mStartButton.setEnabled(false); /** * Connection to WCS server is closed with method Session.disconnect(). */ session.disconnect(); |
14. Получение события, подтверждающего разъединение.
session.onDisconnection() код
Code Block | ||||
---|---|---|---|---|
| ||||
@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); } }); } |