Cameras¶
The cameras on Iristick smart glasses are accessible only through the camera package. This package contains a set of classes and interfaces for opening camera devices, starting capture sessions and capturing frames with capture requests.
This package is loosely modeled after the standard Android camera2
API.
Example code
The Camera Example provided in the SDK package shows how to use the Camera API. It shows a preview of both cameras on the display. The example shows how to adjust zoom and offset, and how to trigger auto-focus.
Querying cameras¶
Every camera device has a unique identifier. You can find the identifier of a
camera with the findCamera()
method of a Headset
object,
providing the desired type of camera.
@Override
public void onHeadsetConnected(@NonNull Headset headset) {
String center = headset.findCamera(CameraCharacteristics.TYPE_WIDE_ANGLE);
if (center != null) {
// This headset has a wide-angle center camera.
// Do something with it.
}
String zoom = headset.findCamera(CameraCharacteristics.TYPE_ZOOM);
if (zoom != null) {
// This headset has a zoom camera.
// Do something with it.
}
}
Alternatively, you can get a list of all available camera devices with the
getCameraIdList()
method of a Headset
object.
With an identifier, you can query the properties and capabilities of the
corresponding camera with
getCameraCharacteristics()
.
/**
* Test if the camera device identified by cameraId supports 720p streaming.
*/
boolean supportsHD(@NonNull Headset headset, @NonNull String cameraId) {
/* Get the characteristics for this specific camera. */
CameraCharacteristics characteristics =
headset.getCameraCharacteristics(cameraId);
/* Query the supported stream mapping. */
StreamConfigurationMap mapping =
characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
/* Test if 720p is in the list of supported sizes. */
for (Point size : mapping.getSizes()) {
if (size.x == 1280 && size.y == 720)
return true;
}
return false;
}
Opening a camera¶
The camera device is opened asynchronously with a call to
openCamera()
, supplying the identifier of the camera and an
implementation of CameraDevice.Listener
.
The listener's onOpened
method will be called when the camera is successfully
opened.
Important
Opening a camera device requires the CAMERA permission.
You can close the camera device with its close()
method.
This will close the active capture session.
When another app has already opened a camera device, that app will be disconnected from the camera device and must reopen it in order to capture images again.
@Override
public void onHeadsetConnected(@NonNull Headset headset) {
/* Open wide-angle center camera. */
String cameraId = headset.findCamera(CameraCharacteristics.TYPE_WIDE_ANGLE);
if (cameraId == null)
return;
headset.openCamera(cameraId, new CameraDevice.Listener() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
/* This is called when the camera was successfully opened.
* You can start a capture session here. */
}
@Override
public void onClosed(@NonNull CameraDevice camera) {
/* This is called when the camera device is completely closed. */
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
/* This is called when the camera is no longer available. */
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
/* This is called when there is an error with the camera device. */
}
}, null);
}
Starting a capture session¶
After opening the camera, it must be configured for the output surfaces that
are going to be used. The configuration is done by starting a capture session
with the createCaptureSession()
method. Any output
surface that will be used as target of a capture request must be provided when
creating the capture session. As for opening a camera device, the creation
of a capture session happens asynchronously. The onConfigured()
method of the
provided listener will be called when the capture session is ready to accept
capture requests.
The following surface formats are supported. Providing a surface with an
unsupported format will result in onConfigureFailed()
to be called with error
code ERROR_INVALID_OUTPUTS
.
Surface format | Pixel format |
---|---|
PixelFormat.RGB_888 |
24 bits per pixel (Red, Green, Blue) |
PixelFormat.RGBA_8888 |
32 bits per pixel (Red, Green, Blue, Alpha) |
PixelFormat.RGBX_8888 |
32 bits per pixel (Red, Green, Blue, Padding) |
PixelFormat.RGB_565 |
16 bits per pixel (5b Red, 6b Green, 5b Blue) |
PixelFormat.L_8 |
Monochrome 8 bits per pixel |
ImageFormat.Y8 |
Monochrome 8 bits per pixel |
ImageFormat.YUY2 |
Interleaved YUYV with 4:2:2 chroma subsampling |
ImageFormat.YV12 |
Planar YUV with 4:2:0 chroma subsampling |
ImageFormat.JPEG |
Compressed JPEG |
Only one capture session may be active per camera device. If you create a new session, the previous session will be closed.
You can close the capture session with its close()
method.
This will asynchronously remove any repeating requests and then wait for all
pending capture requests to finish. If you want to stop the session as soon as
possible, you can abort the captures first with
abortCaptures()
.
Surface mPreviewSurface;
@Override
public void onOpened(@NonNull CameraDevice camera) {
List<Surface> outputs = new ArrayList<>();
outputs.add(mPreviewSurface);
camera.createCaptureSession(outputs, new CaptureSession.Listener() {
@Override
public void onConfigured(@NonNull CaptureSession captureSession) {
/* This is called when the capture session is fully configured.
* You may start issuing capture requests here. */
}
@Override
public void onConfigureFailed(@NonNull CaptureSession captureSession,
int error) {
/* This is called when configuring the capture session failed. */
}
@Override
public void onClosed(@NonNull CaptureSession captureSession) {
/* This is called when the capture session is closed.
* It is no longer possible to issue capture requests. */
}
@Override
public void onActive(@NonNull CaptureSession session) {
/* This is called when the camera device actively starts processing
* capture requests. */
}
@Override
public void onCaptureQueueEmpty(@NonNull CaptureSession session) {
/* This is called when the camera device has no more pending capture
* requests and may fall back to the repeating request if set. */
}
@Override
public void onReady(@NonNull CaptureSession session) {
/* This is called when the camera device has finished processing all
* capture requests and has no repeating request set. */
}
}, null);
}
Creating a capture request¶
Once the capture session is configured, the camera device is ready to process capture requests. A capture request allows to capture an individual frame of the camera. It may be submitted multiple times, or set as a repeating request, to capture consecutive frames.
A capture request specifies the target surfaces and specific camera settings.
Capture requests are created with a CaptureRequest.Builder
.
The createCaptureRequest()
method will create a new
builder and configure it for a specific use case. Then, you can set the output
surfaces and fine tune the settings. Finally, use the
build()
method to create an immutable capture
request.
CaptureRequest createCaptureRequest(CameraDevice camera) {
/* Create the builder. */
CaptureRequest.Builder builder =
camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
/* Add the target surface(s) */
builder.addTarget(mPreviewSurface);
/* Set capture settings (e.g., zoom level). */
builder.set(CaptureRequest.SCALER_ZOOM, 2);
/* Build the capture request. */
return builder.build();
}
You can now submit capture requests to the capture session. For one-shot image
captures or a small burst of captures, use capture()
or
captureBurst()
.
For repeated captures, such as video recording or a camera preview, use
setRepeatingRequest()
.
You can stop the repeating request with stopRepeating()
.
In order to be notified of the progress of the submitted capture requests, you
can provide a CaptureListener2
. A capture sequence denotes
a set of capture requests that have been submitted together.
The capture listener allows tracking the progress of both individual captures
and complete sequence.
class MyCaptureListener extends CaptureListener2 {
@Override
public void onCaptureStarted(@NonNull CaptureSession session,
@NonNull CaptureRequest request,
long frameNumber) {
/* This is called when the camera device starts processing a capture
* request. This is the appropriate time to play a shutter sound
* effect or trigger UI indicators. */
}
@Override
public void onCaptureBufferLost(@NonNull CaptureSession session,
@NonNull CaptureRequest request,
@NonNull Surface surface,
long frameNumber) {
/* This is called when a buffer for capture data could not be sent to
* its destination surface. */
}
@Override
public void onCaptureCompleted(@NonNull CaptureSession session,
@NonNull CaptureRequest request,
@NonNull CaptureResult result) {
/* This is called when an image capture has completed successfully. */
}
@Override
public void onPostProcessCompleted(@NonNull CaptureSession session,
@NonNull CaptureRequest request,
@NonNull CaptureResult result) {
/* This may be called after onCaptureCompleted when post-processing
* has been requested, e.g., when scanning barcodes. The result object
* will contain the same information as onCaptureCompleted plus the
* results of the post-processing. */
}
@Override
public void onCaptureFailed(@NonNull CaptureSession session,
@NonNull CaptureRequest request,
@NonNull CaptureFailure failure) {
/* This is called when a capture failed. */
}
@Override
public void onCaptureSequenceCompleted(@NonNull CaptureSession session,
int sequenceId,
long frameNumber) {
/* This is called when all captures of a capture sequence are
* finished, i.e., onCaptureCompleted or onCaptureFailed has been
* called for all capture requests in the sequence. */
}
@Override
public void onCaptureSequenceAborted(@NonNull CaptureSession session,
int sequenceId) {
/* This is called when a capture sequence aborts before any
* CaptureResult or CaptureFailure for it have been returned
* via this listener. */
}
}