Android Camera Manager¶
Пример приложения, использующего интерфейс Camera1Capturer
для обработки видео при захвате с камеры¶
Данный пример показывает различные варианты использования собственного захвата изображения в одном Android приложении. Пример работает с Android SDK, начиная со сборки 1.1.0.42
На всех скриншотах:
WCS Url
- адрес WCS сервера для установки Websocket соединенияStream name
- имя потока для публикации и воспроизведенияCamera capturer
- выбор примера, реализующего интерфейсCamera1Capturer
Скриншот управления вспышкой:
Turn on flashlight
- кнопка включения/отключения вспышки
Скриншот управления масштабом изображения (zoom in/zoom out):
- масштаб регулируется ползунком
Скриншот использования GPUImage фильтра сепия
:
Use filter
- применить фильтр к захватываемому изображению
Скриншот наложения PNG картинки на изображение:
Select PNG
- кнопка выбора файла PNG картинки из галереи устройстваUse PNG overlay
- применить наложение PNG картинкиX Position
,Y position
- координаты верхнего левого угла PNG картинки в кадреPng width
- ширина PNG картинки в кадре в пикселяхPng height
- высота PNG картинки в кадре в пикселях
Скриншот выбора разрешения публикации:
Camera resolution
- селектор выбора разрешения из поддерживаемых камерой
Работа с кодом примера¶
Для разбора кода возьмем следующие классы примера camera-manager
, который доступен для скачивания в сборке 1.1.0.47:
- класс основной активности приложения CameraManagerActivity.java
- класс реализации интерфейса
Camera1Capturer
примера Zoom ZoomCameraCapturer.java - класс реализации интерфейса
Camera1Enumerator
примера Zoom ZoomCameraEnumerator.java - класс реализации интерфейса
CameraSession
примера Zoom ZoomCameraSession.java - класс реализации интерфейса
Camera1Capturer
примера GPUImage GPUImageCameraCapturer.java - класс реализации интерфейса
Camera1Enumerator
примера GPUImage GPUImageCameraEnumerator.java - класс реализации интерфейса
CameraSession
примера GPUImage GPUImageCameraSession.java - класс реализации интерфейса
Camera1Capturer
примера PngOverlay PngOverlayCameraCapturer.java - класс реализации интерфейса
Camera1Enumerator
примера PngOverlay PngOverlayCameraEnumerator.java - класс реализации интерфейса
CameraSession
примера PngOverlay PngOverlayCameraSession.java - класс реализации интерфейса
Camera1Capturer
примера Resolution ResolutionCameraCapturer.java - класс реализации интерфейса
Camera1Enumerator
примера Resolution ResolutionCameraEnumerator.java - класс реализации интерфейса
CameraSession
примера Resolution ResolutionCameraSession.java
Обратите внимание, что классы реализации интерфейсов помещены в пакет org.webrtc
, это необходимо для доступа к функциям захвата видео и управления камерой
1. Инициализация API¶
Flashphoner.init()
code
2. Создание сессии¶
Flashphoner.createSession()
code
Методу передается объект SessionOptions
со следующими параметрами:
- URL WCS-сервера
SurfaceViewRenderer localRenderer
, который будет использоваться для отображения публикуемого потока (после применения изменений)SurfaceViewRenderer remoteRenderer
, который будет использоваться для отображения воспроизводимого потока
sessionOptions = new SessionOptions(mWcsUrlView.getText().toString());
sessionOptions.setLocalRenderer(localRender);
sessionOptions.setRemoteRenderer(remoteRender);
/**
* Session for connection to WCS server is created with method createSession().
*/
session = Flashphoner.createSession(sessionOptions);
3. Подключение к серверу¶
Session.connect()
code
4. Получение от сервера события, подтверждающего успешное соединение¶
session.onConnected()
code
@Override
public void onConnected(final Connection connection) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mStatusView.setText(connection.getStatus());
...
}
});
});
5. Получение идентификатора тыловой камеры¶
Flashphoner.getMediaDevices().getVideoList()
, Flashphoner.getCameraEnumerator().isBackFacing()
code
int cameraId = 0;
List<MediaDevice> videoList = Flashphoner.getMediaDevices().getVideoList();
for (MediaDevice videoDevice : videoList) {
String videoDeviceName = videoDevice.getLabel();
if (Flashphoner.getCameraEnumerator().isBackFacing(videoDeviceName)) {
cameraId = videoDevice.getId();
break;
}
}
6. Настройка ограничений и создание потока¶
StreamOptions.setConstraints()
, Session.createStream()
code
StreamOptions streamOptions = new StreamOptions(streamName);
VideoConstraints videoConstraints = new VideoConstraints();
videoConstraints.setVideoFps(25);
videoConstraints.setCameraId(cameraId);
Constraints constraints = new Constraints(true, true);
constraints.setVideoConstraints(videoConstraints);
streamOptions.setConstraints(constraints);
/**
* Stream is created with method Session.createStream().
*/
publishStream = session.createStream(streamOptions);
7. Запрос прав на публикацию потока¶
ActivityCompat.requestPermissions()
code
@Override
public void onConnected(final Connection connection) {
runOnUiThread(new Runnable() {
@Override
public void run() {
...
ActivityCompat.requestPermissions(StreamingMinActivity.this,
new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA},
PUBLISH_REQUEST_CODE);
...
}
...
});
});
8. Публикация потока после предоставления соответствующих прав¶
Stream.publish()
code
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String permissions[], @NonNull int[] grantResults) {
switch (requestCode) {
case PUBLISH_REQUEST_CODE: {
if (grantResults.length == 0 ||
grantResults[0] != PackageManager.PERMISSION_GRANTED ||
grantResults[1] != PackageManager.PERMISSION_GRANTED) {
muteButton();
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;
}
...
}
}
9. Воспроизведение потока после успешной публикации¶
Session.createStream()
, Stream.play()
code
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(true, true));
/**
* Stream is created with method Session.createStream().
*/
playStream = session.createStream(streamOptions);
...
/**
* Method Stream.play() is called to start playback of the stream.
*/
playStream.play();
} else {
Log.e(TAG, "Can not publish stream " + stream.getName() + " " + streamStatus);
onStopped();
}
mStatusView.setText(streamStatus.toString());
}
});
}
});
10. Закрытие соединения¶
Session.disconnect()
[code](https://github.com/flashphoner/wcs-android-sdk-samples/blob/ba761704663de2602632d9f37a2af2f6865cb3e0/camera-manager/src/main/java/com/example/camera_manager/CameraManagerActivity.java#L552
mStartButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
muteButton();
if (mStartButton.getTag() == null || Integer.valueOf(R.string.action_start).equals(mStartButton.getTag())) {
...
} else {
/**
* Connection to WCS server is closed with method Session.disconnect().
*/
session.disconnect();
}
...
}
});
11. Получение события, подтверждающего разъединение¶
session.onDisconnection()
code
@Override
public void onDisconnection(final Connection connection) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mStatusView.setText(connection.getStatus());
mStatusView.setText(connection.getStatus());
onStopped();
}
});
}
12. Выбор примера¶
mCameraCapturer.setOnItemChosenListener(new LabelledSpinner.OnItemChosenListener() {
@Override
public void onItemChosen(View labelledSpinner, AdapterView<?> adapterView, View itemView, int position, long id) {
String captureType = getResources().getStringArray(R.array.camera_capturer)[position];
switch (captureType) {
case "Flashlight":
changeFlashlightCamera();
break;
case "Zoom":
changeZoomCamera();
break;
case "GPUImage":
changeGpuImageCamera();
break;
case "PNG overlay":
changePngOverlayCamera();
break;
}
}
@Override
public void onNothingChosen(View labelledSpinner, AdapterView<?> adapterView) {
}
});
13. Установка способа захвата камеры и необходимых опций захвата¶
private void changeFlashlightCamera() {
CameraCapturerFactory.getInstance().setCameraType(CameraCapturerFactory.CameraType.FLASHLIGHT_CAMERA);
...
}
private void changeZoomCamera() {
CameraCapturerFactory.getInstance().setCustomCameraCapturerOptions(zoomCameraCapturerOptions);
CameraCapturerFactory.getInstance().setCameraType(CameraCapturerFactory.CameraType.CUSTOM);
...
}
private void changePngOverlayCamera() {
CameraCapturerFactory.getInstance().setCustomCameraCapturerOptions(pngOverlayCameraCapturerOptions);
CameraCapturerFactory.getInstance().setCameraType(CameraCapturerFactory.CameraType.CUSTOM);
...
}
private void changeGpuImageCamera() {
CameraCapturerFactory.getInstance().setCustomCameraCapturerOptions(gpuImageCameraCapturerOptions);
CameraCapturerFactory.getInstance().setCameraType(CameraCapturerFactory.CameraType.CUSTOM);
...
}
14. Настройка опций объекта захвата камеры для примера Zoom¶
private CustomCameraCapturerOptions zoomCameraCapturerOptions = new CustomCameraCapturerOptions() {
private String cameraName;
private CameraVideoCapturer.CameraEventsHandler eventsHandler;
private boolean captureToTexture;
@Override
public Class<?>[] getCameraConstructorArgsTypes() {
return new Class<?>[]{String.class, CameraVideoCapturer.CameraEventsHandler.class, boolean.class};
}
@Override
public Object[] getCameraConstructorArgs() {
return new Object[]{cameraName, eventsHandler, captureToTexture};
}
@Override
public void setCameraName(String cameraName) {
this.cameraName = cameraName;
}
@Override
public void setEventsHandler(CameraVideoCapturer.CameraEventsHandler eventsHandler) {
this.eventsHandler = eventsHandler;
}
@Override
public void setCaptureToTexture(boolean captureToTexture) {
this.captureToTexture = captureToTexture;
}
@Override
public String getCameraClassName() {
return "org.webrtc.ZoomCameraCapturer";
}
@Override
public Class<?>[] getEnumeratorConstructorArgsTypes() {
return new Class[0];
}
@Override
public Object[] getEnumeratorConstructorArgs() {
return new Object[0];
}
@Override
public String getEnumeratorClassName() {
return "org.webrtc.ZoomCameraEnumerator";
}
};
15. Настройка опций объекта захвата камеры для примера PngOverlay¶
private CustomCameraCapturerOptions pngOverlayCameraCapturerOptions = new CustomCameraCapturerOptions() {
private String cameraName;
private CameraVideoCapturer.CameraEventsHandler eventsHandler;
private boolean captureToTexture;
@Override
public Class<?>[] getCameraConstructorArgsTypes() {
return new Class<?>[]{String.class, CameraVideoCapturer.CameraEventsHandler.class, boolean.class};
}
@Override
public Object[] getCameraConstructorArgs() {
return new Object[]{cameraName, eventsHandler, captureToTexture};
}
@Override
public void setCameraName(String cameraName) {
this.cameraName = cameraName;
}
@Override
public void setEventsHandler(CameraVideoCapturer.CameraEventsHandler eventsHandler) {
this.eventsHandler = eventsHandler;
}
@Override
public void setCaptureToTexture(boolean captureToTexture) {
this.captureToTexture = captureToTexture;
}
@Override
public String getCameraClassName() {
return "org.webrtc.PngOverlayCameraCapturer";
}
@Override
public Class<?>[] getEnumeratorConstructorArgsTypes() {
return new Class[0];
}
@Override
public Object[] getEnumeratorConstructorArgs() {
return new Object[0];
}
@Override
public String getEnumeratorClassName() {
return "org.webrtc.PngOverlayCameraEnumerator";
}
};
16. Настройка опций объекта захвата камеры для примера GPUImage¶
private CustomCameraCapturerOptions gpuImageCameraCapturerOptions = new CustomCameraCapturerOptions() {
private String cameraName;
private CameraVideoCapturer.CameraEventsHandler eventsHandler;
private boolean captureToTexture;
@Override
public Class<?>[] getCameraConstructorArgsTypes() {
return new Class<?>[]{String.class, CameraVideoCapturer.CameraEventsHandler.class, boolean.class};
}
@Override
public Object[] getCameraConstructorArgs() {
return new Object[]{cameraName, eventsHandler, captureToTexture};
}
@Override
public void setCameraName(String cameraName) {
this.cameraName = cameraName;
}
@Override
public void setEventsHandler(CameraVideoCapturer.CameraEventsHandler eventsHandler) {
this.eventsHandler = eventsHandler;
}
@Override
public void setCaptureToTexture(boolean captureToTexture) {
this.captureToTexture = captureToTexture;
}
@Override
public String getCameraClassName() {
return "org.webrtc.GPUImageCameraCapturer";
}
@Override
public Class<?>[] getEnumeratorConstructorArgsTypes() {
return new Class[0];
}
@Override
public Object[] getEnumeratorConstructorArgs() {
return new Object[0];
}
@Override
public String getEnumeratorClassName() {
return "org.webrtc.GPUImageCameraEnumerator";
}
};
17. Настройка опций объекта захвата камеры для примера Resolution¶
private CustomCameraCapturerOptions resolutionCameraCapturerOptions = new CustomCameraCapturerOptions() {
private String cameraName;
private CameraVideoCapturer.CameraEventsHandler eventsHandler;
private boolean captureToTexture;
@Override
public Class<?>[] getCameraConstructorArgsTypes() {
return new Class<?>[]{String.class, CameraVideoCapturer.CameraEventsHandler.class, boolean.class};
}
@Override
public Object[] getCameraConstructorArgs() {
return new Object[]{cameraName, eventsHandler, captureToTexture};
}
@Override
public void setCameraName(String cameraName) {
this.cameraName = cameraName;
}
@Override
public void setEventsHandler(CameraVideoCapturer.CameraEventsHandler eventsHandler) {
this.eventsHandler = eventsHandler;
}
@Override
public void setCaptureToTexture(boolean captureToTexture) {
this.captureToTexture = captureToTexture;
}
@Override
public String getCameraClassName() {
return "org.webrtc.ResolutionCameraCapturer";
}
@Override
public Class<?>[] getEnumeratorConstructorArgsTypes() {
return new Class[0];
}
@Override
public Object[] getEnumeratorConstructorArgs() {
return new Object[0];
}
@Override
public String getEnumeratorClassName() {
return "org.webrtc.ResolutionCameraEnumerator";
}
};
18. Включение вспышки¶
Flashphoner.turnOnFlashlight()
code
private void turnOnFlashlight() {
if (Flashphoner.turnOnFlashlight()) {
mSwitchFlashlightButton.setText(getResources().getString(R.string.turn_off_flashlight));
flashlight = true;
}
}
19. Отключение вспышки¶
Flashphoner.turnOffFlashlight()
code
private void turnOffFlashlight() {
Flashphoner.turnOffFlashlight();
mSwitchFlashlightButton.setText(getResources().getString(R.string.turn_on_flashlight));
flashlight = false;
}
20. Управление масштабом при помощи ползунка¶
ZoomCameraCapturer.setZoom()
code
mZoomSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
CameraVideoCapturer cameraVideoCapturer = CameraCapturerFactory.getInstance().getCameraVideoCapturer();
if (cameraVideoCapturer instanceof ZoomCameraCapturer) {
((ZoomCameraCapturer) cameraVideoCapturer).setZoom(progress);
}
}
...
});
21. Добавление картинки в поток с запросом прав¶
PngOverlayCameraCapturer.setPicture()
code
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
InputStream inputStream = null;
try {
inputStream = CameraManagerActivity.this.getBaseContext().getContentResolver().openInputStream(data.getData());
} catch (FileNotFoundException e) {
Log.e(TAG, "Can't select picture: " + e.getMessage());
}
picture = BitmapFactory.decodeStream(inputStream);
}
CameraVideoCapturer cameraVideoCapturer = CameraCapturerFactory.getInstance().getCameraVideoCapturer();
if (cameraVideoCapturer instanceof PngOverlayCameraCapturer && picture != null) {
((PngOverlayCameraCapturer) cameraVideoCapturer).setPicture(picture);
}
}
22. Установка выбранного разрешения публикации¶
mCameraResolutionSpinner = (LabelledSpinner) findViewById(R.id.camera_resolution_spinner);
mCameraResolutionSpinner.setOnItemChosenListener(new LabelledSpinner.OnItemChosenListener() {
@Override
public void onItemChosen(View labelledSpinner, AdapterView<?> adapterView, View itemView, int position, long id) {
String resolution = adapterView.getSelectedItem().toString();
if (resolution.isEmpty()) {
return;
}
setResolutions(resolution);
}
@Override
public void onNothingChosen(View labelledSpinner, AdapterView<?> adapterView) {
}
});
...
private void setResolutions(String resolutionStr) {
String[] resolution = resolutionStr.split("x");
mWidth.setText(resolution[0]);
mHeight.setText(resolution[1]);
}
23. Создание сессии камеры в классе ZoomCameraCapturer¶
CameraSession.create()
code
@Override
protected void createCameraSession(CameraSession.CreateSessionCallback createSessionCallback, CameraSession.Events events, Context applicationContext, SurfaceTextureHelper surfaceTextureHelper, String cameraName, int width, int height, int framerate) {
CameraSession.CreateSessionCallback myCallback = new CameraSession.CreateSessionCallback() {
@Override
public void onDone(CameraSession cameraSession) {
ZoomCameraCapturer.this.cameraSession = (ZoomCameraSession) cameraSession;
createSessionCallback.onDone(cameraSession);
}
@Override
public void onFailure(CameraSession.FailureType failureType, String s) {
createSessionCallback.onFailure(failureType, s);
}
};
ZoomCameraSession.create(myCallback, events, captureToTexture, applicationContext, surfaceTextureHelper, Camera1Enumerator.getCameraIndex(cameraName), width, height, framerate);
}
24. Изменение масштаба в классе ZoomCameraCapturer
¶
CameraSession.setZoom()
code
25. Выделение буфера для захвата камеры в классе ZoomCameraSession
¶
if (!captureToTexture) {
int frameSize = captureFormat.frameSize();
//The implementation is taken from the WebRTC library, so the purpose of the three buffers is not entirely known
for(int i = 0; i < 3; ++i) {
ByteBuffer buffer = ByteBuffer.allocateDirect(frameSize);
camera.addCallbackBuffer(buffer.array());
}
}
26. Реализация изменение масштаба в классе ZoomCameraSession
¶
public boolean setZoom(int value) {
if (!isCameraActive() && camera.getParameters().isZoomSupported()) {
return false;
}
Camera.Parameters parameters = camera.getParameters();
parameters.setZoom(value);
camera.setParameters(parameters);
return true;
}
27. Установка использования фильтра в классе GPUImageCameraSession
¶
28. Применение фильтра к данным из буфера камеры¶
private void listenForBytebufferFrames() {
this.camera.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() {
public void onPreviewFrame(byte[] data, Camera callbackCamera) {
GPUImageCameraSession.this.checkIsOnCameraThread();
if (callbackCamera != GPUImageCameraSession.this.camera) {
Logging.e(TAG, CALLBACK_FROM_A_DIFFERENT_CAMERA_THIS_SHOULD_NEVER_HAPPEN);
} else if (GPUImageCameraSession.this.state != GPUImageCameraSession.SessionState.RUNNING) {
Logging.d(TAG, BYTEBUFFER_FRAME_CAPTURED_BUT_CAMERA_IS_NO_LONGER_RUNNING);
} else {
...
applyFilter(data, GPUImageCameraSession.this.captureFormat.width, GPUImageCameraSession.this.captureFormat.height);
VideoFrame.Buffer frameBuffer = new NV21Buffer(data, GPUImageCameraSession.this.captureFormat.width, GPUImageCameraSession.this.captureFormat.height, () -> {
GPUImageCameraSession.this.cameraThreadHandler.post(() -> {
if (GPUImageCameraSession.this.state == GPUImageCameraSession.SessionState.RUNNING) {
GPUImageCameraSession.this.camera.addCallbackBuffer(data);
}
});
});
VideoFrame frame = new VideoFrame(frameBuffer, GPUImageCameraSession.this.getFrameOrientation(), captureTimeNs);
GPUImageCameraSession.this.events.onFrameCaptured(GPUImageCameraSession.this, frame);
frame.release();
}
}
});
}
29. Реализация фильтра¶
private void initFilter(int width, int height) {
filter = new GPUImageMonochromeFilter();
filter.setColor(0,0,0);
renderer = new GPUImageRenderer(filter);
renderer.setRotation(Rotation.NORMAL, false, false);
renderer.setScaleType(GPUImage.ScaleType.CENTER_INSIDE);
buffer = new PixelBuffer(width, height);
buffer.setRenderer(renderer);
}
private void destroyFilter() {
filter.destroy();
buffer.destroy();
}
private void applyFilter(byte[] data, int width, int height) {
if (!isUsedFilter) {
return;
}
renderer.onPreviewFrame(data, width, height);
Bitmap newBitmapRgb = buffer.getBitmap();
byte[] dataYuv = Utils.getNV21(width, height, newBitmapRgb);
System.arraycopy(dataYuv, 0, data, 0, dataYuv.length);
}
30. Установка картинки для наложения в классе PngOverlayCameraCapturer
¶
public void setPicture(Bitmap picture) {
if (cameraSession != null) {
cameraSession.setPicture(picture);
}
}
31. Наложение данных картинки на данные из буфера камеры¶
private void listenForBytebufferFrames() {
this.camera.setPreviewCallbackWithBuffer(new Camera.PreviewCallback() {
public void onPreviewFrame(byte[] data, Camera callbackCamera) {
PngOverlayCameraSession.this.checkIsOnCameraThread();
if (callbackCamera != PngOverlayCameraSession.this.camera) {
Logging.e(TAG, CALLBACK_FROM_A_DIFFERENT_CAMERA_THIS_SHOULD_NEVER_HAPPEN);
} else if (PngOverlayCameraSession.this.state != PngOverlayCameraSession.SessionState.RUNNING) {
Logging.d(TAG, BYTEBUFFER_FRAME_CAPTURED_BUT_CAMERA_IS_NO_LONGER_RUNNING);
} else {
...
insertPicture(data, PngOverlayCameraSession.this.captureFormat.width, PngOverlayCameraSession.this.captureFormat.height);
VideoFrame.Buffer frameBuffer = new NV21Buffer(data, PngOverlayCameraSession.this.captureFormat.width, PngOverlayCameraSession.this.captureFormat.height, () -> {
PngOverlayCameraSession.this.cameraThreadHandler.post(() -> {
if (PngOverlayCameraSession.this.state == PngOverlayCameraSession.SessionState.RUNNING) {
PngOverlayCameraSession.this.camera.addCallbackBuffer(data);
}
});
});
VideoFrame frame = new VideoFrame(frameBuffer, PngOverlayCameraSession.this.getFrameOrientation(), captureTimeNs);
PngOverlayCameraSession.this.events.onFrameCaptured(PngOverlayCameraSession.this, frame);
frame.release();
}
}
});
}
32. Реализация наложения картинки¶
private void insertPicture(byte[] data, int width, int height) {
if (picture == null || !isUsedPngOverlay) {
return;
}
Bitmap scaledPicture = rescalingPicture();
int [] pngArray = new int[scaledPicture.getHeight() * scaledPicture.getWidth()];
scaledPicture.getPixels(pngArray, 0, scaledPicture.getWidth(), 0, 0, scaledPicture.getWidth(), scaledPicture.getHeight());
int [] rgbData = new int [width * height];
GPUImageNativeLibrary.YUVtoARBG(data, width, height, rgbData);
int pictureW = scaledPicture.getWidth();
int pictureH = scaledPicture.getHeight();
for (int c = 0; c < pngArray.length; c++) {
int pictureColumn = c / pictureW;
int pictureLine = c - pictureColumn * pictureW;
int index = (pictureLine * width) + pictureColumn + startX * width + startY;
if (index >= data.length) {
break;
}
rgbData[index] = pngArray[c];
}
byte[] yuvData = Utils.getNV21(width, height, rgbData);
System.arraycopy(yuvData, 0, data, 0, yuvData.length);
}
33. Получение списка поддерживаемых разрешений¶
ResolutionCameraCapturer.getSupportedResolutions()
code