Android
Camera 2 API
Sök…
parametrar
Parameter | detaljer |
---|---|
CameraCaptureSession | En konfigurerad inspelningssession för en CameraDevice , som används för att ta bilder från kameran eller bearbeta bilder som tagits från kameran i samma session tidigare |
CameraDevice | En representation av en enda kamera ansluten till en Android-enhet |
CameraCharacteristics | Egenskaperna som beskriver en CameraDevice. Dessa egenskaper är fixerade för en given CameraDevice och kan frågas via CameraManager-gränssnittet med getCameraCharacteristics(String) |
CameraManager | En systemtjänstchef för att upptäcka, karakterisera och ansluta till CameraDevices . Du kan få en instans av den här klassen genom att ringa Context.getSystemService() |
CaptureRequest | Ett oändligt paket med inställningar och utgångar som behövs för att fånga en enda bild från kameran. Innehåller konfigurationen för infångningsmaskinvaran (sensor, lins, blixt), behandlingsrörledningen, kontrollalgoritmerna och utgångsbuffertarna. Innehåller också listan med målytor att skicka bilddata till för den här fången. Kan skapas med hjälp av en CaptureRequest.Builder instans, erhållen genom att ringa createCaptureRequest(int) |
CaptureResult | Delmängden av resultaten från en enda bildinspelning från bildsensorn. Innehåller en delmängd av den slutliga konfigurationen för infångningsmaskinvaran (sensor, lins, blixt), behandlingsrörledningen, styralgoritmerna och utgångsbuffertarna. Den produceras av en CameraDevice efter bearbetning av en CaptureRequest |
Anmärkningar
- Camera2-API: er finns i API 21+ (Lollipop och senare)
- Även om en Android-enhet har en 21+ ROM officiellt, finns det ingen garanti för att den implementerar Camera2 API: er, det är helt upp till tillverkaren att implementera den eller inte (Exempel: LG G2 har officiellt Lollipop-stöd, men inga Camera2 API: er)
- Med Camera2 försvinner kameran ("Camera1")
- Med stor kraft kommer stort ansvar: Det är lättare att röra det när du använder dessa API: er.
- Kom ihåg att om du bara vill ta ett foto i din app och helt enkelt få det, behöver du inte implementera Camera2, du kan öppna enhetens kameraapp via en avsikt och ta emot den tillbaka
Förhandsgranska huvudkameran i en TextureView
I det här fallet, bygga mot API 23, så behörigheter hanteras också.
Du måste lägga till följande manifest i manifestet (var du än använder API-nivån):
<uses-permission android:name="android.permission.CAMERA"/>
Vi håller på att skapa en aktivitet (Camera2Activity.java) som fyller en TextureView
med förhandsgranskningen av enhetens kamera.
Aktiviteten vi ska använda är en typisk AppCompatActivity:
public class Camera2Activity extends AppCompatActivity {
Attribut (Du kanske måste läsa hela exemplet för att förstå en del av det)
MAX_PREVIEW_SIZE
garanterat av Camera2 API är 1920x1080
private static final int MAX_PREVIEW_WIDTH = 1920;
private static final int MAX_PREVIEW_HEIGHT = 1080;
TextureView.SurfaceTextureListener
hanterar flera livscykelhändelser på en TextureView
. I det här fallet lyssnar vi på händelserna. När SurfaceTexture är klar initialiserar vi kameran. När storleken ändras ställer vi in förhandsgranskningen från kameran
private final TextureView.SurfaceTextureListener mSurfaceTextureListener
= new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
openCamera(width, height);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
configureTransform(width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture texture) {
}
};
En CameraDevice
representerar en fysisk enhets kamera. I detta attribut sparar vi ID för den aktuella CameraDevice
private String mCameraId;
Detta är den vy ( TextureView
) som vi kommer att använda för att "rita" förhandsvisningen av kameran
private TextureView mTextureView;
CameraCaptureSession
för förhandsvisning av kameran
private CameraCaptureSession mCaptureSession;
En hänvisning till den öppnade CameraDevice
private CameraDevice mCameraDevice;
Size
på kameraförhandsvisning.
private Size mPreviewSize;
CameraDevice.StateCallback
anropas när CameraDevice
ändrar sitt tillstånd
private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice cameraDevice) {
// This method is called when the camera is opened. We start camera preview here.
mCameraOpenCloseLock.release();
mCameraDevice = cameraDevice;
createCameraPreviewSession();
}
@Override
public void onDisconnected(@NonNull CameraDevice cameraDevice) {
mCameraOpenCloseLock.release();
cameraDevice.close();
mCameraDevice = null;
}
@Override
public void onError(@NonNull CameraDevice cameraDevice, int error) {
mCameraOpenCloseLock.release();
cameraDevice.close();
mCameraDevice = null;
finish();
}
};
En ytterligare tråd för att köra uppgifter som inte bör blockera användargränssnittet
private HandlerThread mBackgroundThread;
En Handler
för att köra uppgifter i bakgrunden
private Handler mBackgroundHandler;
En ImageReader
som hanterar stillbildsfångst
private ImageReader mImageReader;
CaptureRequest.Builder
för förhandsvisning av kameran
private CaptureRequest.Builder mPreviewRequestBuilder;
CaptureRequest
genereras av mPreviewRequestBuilder
private CaptureRequest mPreviewRequest;
En Semaphore
att förhindra att appen lämnar innan kameran stängs.
private Semaphore mCameraOpenCloseLock = new Semaphore(1);
Konstant ID för tillståndsförfrågan
private static final int REQUEST_CAMERA_PERMISSION = 1;
Android-livscykelmetoder
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera2);
mTextureView = (TextureView) findViewById(R.id.texture);
}
@Override
public void onResume() {
super.onResume();
startBackgroundThread();
// When the screen is turned off and turned back on, the SurfaceTexture is already
// available, and "onSurfaceTextureAvailable" will not be called. In that case, we can open
// a camera and start preview from here (otherwise, we wait until the surface is ready in
// the SurfaceTextureListener).
if (mTextureView.isAvailable()) {
openCamera(mTextureView.getWidth(), mTextureView.getHeight());
} else {
mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
}
}
@Override
public void onPause() {
closeCamera();
stopBackgroundThread();
super.onPause();
}
Camera2-relaterade metoder
Dessa är metoder som använder Camera2 API: er
private void openCamera(int width, int height) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
requestCameraPermission();
return;
}
setUpCameraOutputs(width, height);
configureTransform(width, height);
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Time out waiting to lock camera opening.");
}
manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera opening.", e);
}
}
Stänger den aktuella kameran
private void closeCamera() {
try {
mCameraOpenCloseLock.acquire();
if (null != mCaptureSession) {
mCaptureSession.close();
mCaptureSession = null;
}
if (null != mCameraDevice) {
mCameraDevice.close();
mCameraDevice = null;
}
if (null != mImageReader) {
mImageReader.close();
mImageReader = null;
}
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
} finally {
mCameraOpenCloseLock.release();
}
}
Ställer in medlemsvariabler relaterade till kamera
private void setUpCameraOutputs(int width, int height) {
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
for (String cameraId : manager.getCameraIdList()) {
CameraCharacteristics characteristics
= manager.getCameraCharacteristics(cameraId);
// We don't use a front facing camera in this sample.
Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
if (facing != null && facing == CameraCharacteristics.LENS_FACING_FRONT) {
continue;
}
StreamConfigurationMap map = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
continue;
}
// For still image captures, we use the largest available size.
Size largest = Collections.max(
Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)),
new CompareSizesByArea());
mImageReader = ImageReader.newInstance(largest.getWidth(), largest.getHeight(),
ImageFormat.JPEG, /*maxImages*/2);
mImageReader.setOnImageAvailableListener(
null, mBackgroundHandler);
Point displaySize = new Point();
getWindowManager().getDefaultDisplay().getSize(displaySize);
int rotatedPreviewWidth = width;
int rotatedPreviewHeight = height;
int maxPreviewWidth = displaySize.x;
int maxPreviewHeight = displaySize.y;
if (maxPreviewWidth > MAX_PREVIEW_WIDTH) {
maxPreviewWidth = MAX_PREVIEW_WIDTH;
}
if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) {
maxPreviewHeight = MAX_PREVIEW_HEIGHT;
}
// Danger! Attempting to use too large a preview size could exceed the camera
// bus' bandwidth limitation, resulting in gorgeous previews but the storage of
// garbage capture data.
mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth,
maxPreviewHeight, largest);
mCameraId = cameraId;
return;
}
} catch (CameraAccessException e) {
e.printStackTrace();
} catch (NullPointerException e) {
// Currently an NPE is thrown when the Camera2API is used but not supported on the
// device this code runs.
Toast.makeText(Camera2Activity.this, "Camera2 API not supported on this device", Toast.LENGTH_LONG).show();
}
}
Skapar en ny CameraCaptureSession
för förhandsvisning av kameran
private void createCameraPreviewSession() {
try {
SurfaceTexture texture = mTextureView.getSurfaceTexture();
assert texture != null;
// We configure the size of default buffer to be the size of camera preview we want.
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
// This is the output Surface we need to start preview.
Surface surface = new Surface(texture);
// We set up a CaptureRequest.Builder with the output Surface.
mPreviewRequestBuilder
= mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mPreviewRequestBuilder.addTarget(surface);
// Here, we create a CameraCaptureSession for camera preview.
mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
// The camera is already closed
if (null == mCameraDevice) {
return;
}
// When the session is ready, we start displaying the preview.
mCaptureSession = cameraCaptureSession;
try {
// Auto focus should be continuous for camera preview.
mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE,
CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
// Finally, we start displaying the camera preview.
mPreviewRequest = mPreviewRequestBuilder.build();
mCaptureSession.setRepeatingRequest(mPreviewRequest,
null, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(
@NonNull CameraCaptureSession cameraCaptureSession) {
showToast("Failed");
}
}, null
);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
Tillståndsrelaterade metoder För Android API 23+
private void requestCameraPermission() {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) {
new AlertDialog.Builder(Camera2Activity.this)
.setMessage("R string request permission")
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
ActivityCompat.requestPermissions(Camera2Activity.this,
new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA_PERMISSION);
}
})
.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.create();
} else {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA},
REQUEST_CAMERA_PERMISSION);
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
if (requestCode == REQUEST_CAMERA_PERMISSION) {
if (grantResults.length != 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(Camera2Activity.this, "ERROR: Camera permissions not granted", Toast.LENGTH_LONG).show();
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
Bakgrundstråd / hanteringsmetoder
private void startBackgroundThread() {
mBackgroundThread = new HandlerThread("CameraBackground");
mBackgroundThread.start();
mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
}
private void stopBackgroundThread() {
mBackgroundThread.quitSafely();
try {
mBackgroundThread.join();
mBackgroundThread = null;
mBackgroundHandler = null;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Verktygsmetoder
Med tanke på val av Size
stöds av en kamera väljer du den minsta som är åtminstone stort som respektive texturvystorlek, och som är lika stor som den respektive maxstorleken och vars sidförhållande matchar det angivna värdet. Om det inte finns, välj den största som är högst lika stor som respektive maxstorlek och vars sidförhållande matchar det angivna värdet
private static Size chooseOptimalSize(Size[] choices, int textureViewWidth,
int textureViewHeight, int maxWidth, int maxHeight, Size aspectRatio) {
// Collect the supported resolutions that are at least as big as the preview Surface
List<Size> bigEnough = new ArrayList<>();
// Collect the supported resolutions that are smaller than the preview Surface
List<Size> notBigEnough = new ArrayList<>();
int w = aspectRatio.getWidth();
int h = aspectRatio.getHeight();
for (Size option : choices) {
if (option.getWidth() <= maxWidth && option.getHeight() <= maxHeight &&
option.getHeight() == option.getWidth() * h / w) {
if (option.getWidth() >= textureViewWidth &&
option.getHeight() >= textureViewHeight) {
bigEnough.add(option);
} else {
notBigEnough.add(option);
}
}
}
// Pick the smallest of those big enough. If there is no one big enough, pick the
// largest of those not big enough.
if (bigEnough.size() > 0) {
return Collections.min(bigEnough, new CompareSizesByArea());
} else if (notBigEnough.size() > 0) {
return Collections.max(notBigEnough, new CompareSizesByArea());
} else {
Log.e("Camera2", "Couldn't find any suitable preview size");
return choices[0];
}
}
Den här metoden konfigurerar den nödvändiga Matrix
transformationen till mTextureView
private void configureTransform(int viewWidth, int viewHeight) {
if (null == mTextureView || null == mPreviewSize) {
return;
}
int rotation = getWindowManager().getDefaultDisplay().getRotation();
Matrix matrix = new Matrix();
RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
float centerX = viewRect.centerX();
float centerY = viewRect.centerY();
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
float scale = Math.max(
(float) viewHeight / mPreviewSize.getHeight(),
(float) viewWidth / mPreviewSize.getWidth());
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
} else if (Surface.ROTATION_180 == rotation) {
matrix.postRotate(180, centerX, centerY);
}
mTextureView.setTransform(matrix);
}
Denna metod jämför två Size
baserade på deras områden.
static class CompareSizesByArea implements Comparator<Size> {
@Override
public int compare(Size lhs, Size rhs) {
// We cast here to ensure the multiplications won't overflow
return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
(long) rhs.getWidth() * rhs.getHeight());
}
}
Inte mycket att se här
/**
* Shows a {@link Toast} on the UI thread.
*
* @param text The message to show
*/
private void showToast(final String text) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(Camera2Activity.this, text, Toast.LENGTH_SHORT).show();
}
});
}