카메라 캡처 세션 및 요청

참고: 이 페이지에서는 Camera2 패키지를 다룹니다. 앱에 Camera2의 특정 하위 수준 기능이 필요하지 않다면 CameraX를 사용하는 것이 좋습니다. CameraX와 Camera2는 모두 Android 5.0(API 수준 21) 이상을 지원합니다.

하나의 Android 지원 기기에 여러 대의 카메라가 있을 수 있습니다. 각 카메라는 CameraDevice님, CameraDevice는 동시에 둘 이상의 스트림을 출력할 수 있습니다.

이렇게 하는 이유는 한 스트림, 순차 카메라 프레임이 CameraDevice는 특정 작업에 최적화됩니다. 예를 들면 다음과 같습니다. 사진을 찍거나 동영상을 만드는 데 사용될 수 있습니다. 스트림은 원시 프레임을 처리하는 병렬 파이프라인 역할을 합니다. 한 번에 한 프레임씩 카메라 밖으로 나오면

그림 1. 범용 카메라 앱 빌드 삽화 (Google I/O '18)

병렬 처리는 작업 유형에 따라 성능 제한이 있을 수 있음을 CPU, GPU 또는 기타 프로세서에서 가용한 처리 성능을 보입니다. 만약 수신 프레임을 따라잡을 수 없어서 해당 프레임을 드롭하기 시작합니다.

파이프라인마다 고유한 출력 형식이 있습니다. 들어오는 원시 데이터는 적절한 데이터 세트로 자동 변환하여 출력 형식: 암시적 로직 각 파이프라인과 연결됩니다 이 페이지의CameraDevice 코드 샘플은 특정적이지 않으므로 먼저 이를 열거합니다. 사용 가능한 모든 카메라를 찾습니다.

CameraDevice를 사용하여 CameraCaptureSession, 이는 해당 CameraDevice에만 해당합니다. CameraDeviceCameraCaptureSession를 사용하여 각 원시 프레임의 프레임 구성. 이 구성은 자동 초점, 조리개, 효과, 노출 등이 있습니다 하드웨어 제약으로 인해 단일 구성만 언제든지 카메라 센서에서 활성 상태를 유지하며, 이를 active 구성으로 설정합니다.

하지만 스트림 사용 사례는 CameraDevice를 사용하는 기존의 방식을 개선하고 확장합니다. 캡처 세션을 스트리밍할 수 있습니다. 이렇게 하면 카메라 스트림을 사용할 수 있습니다 예를 들어 배터리 수명을 연장하여 화상 통화를 할 수 있습니다.

CameraCaptureSessionCameraDevice 세션을 만들 때는 파이프라인을 추가하거나 삭제할 수 없습니다. CameraCaptureSessionCaptureRequest, 활성 구성이 됩니다

CaptureRequest는 구성을 큐에 추가하고 구성을 하나 이상 선택합니다. 하나 또는 모든 가용 파이프라인에서 프레임을 수신하여 CameraDevice 캡처 수명 동안 여러 캡처 요청을 보낼 수 있습니다. 세션입니다. 각 요청은 활성 구성 및 출력 집합을 변경할 수 있습니다. 원시 이미지를 수신하는 파이프라인을 빌드합니다

스트림 사용 사례를 통한 성능 개선

스트림 사용 사례는 Camera2 캡처 성능을 개선하는 방법임 세션. 매개변수를 조정할 수 있는 추가 정보를 하드웨어 기기에 제공합니다. 특정 작업에 더 나은 카메라 환경을 제공합니다.

이 카메라 기기가 카메라 하드웨어 및 소프트웨어 파이프라인을 최적화하도록 지원 각 스트림의 사용자 시나리오를 기반으로 합니다 스트림 사용에 대한 자세한 내용은 케이스는 setStreamUseCase를 참고하세요.

스트림 사용 사례를 사용하면 특정 카메라 스트림이 사용되는 방식을 지정할 수 있습니다. 인코더와 디코더에 템플릿을 설정하는 것 외에도 CameraDevice.createCaptureRequest() 이렇게 하면 카메라 하드웨어가 카메라 센서 설정과 같은 미세 조정, 센서 모드 또는 특정 사용 사례에 적합한 품질 또는 지연 시간을 절감할 수 있습니다

스트림 사용 사례에는 다음이 포함됩니다.

  • DEFAULT: 모든 기존 애플리케이션 동작을 포함합니다. 이 값은 스트림 사용 사례를 설정할 수 있습니다

  • PREVIEW: 뷰파인더 또는 인앱 이미지 분석에 권장됩니다.

  • STILL_CAPTURE: 고화질 고해상도 캡처에 최적화되지만 미리보기와 같은 프레임 속도를 유지할 것으로 예상됩니다.

  • VIDEO_RECORD: 고화질을 포함한 고화질 동영상 캡처에 최적화되었습니다. 손떨림 보정(기기에서 지원하고 애플리케이션에서 사용 설정한 경우) 이 옵션은 실시간으로 상당한 지연이 있는 출력 프레임을 생성할 수 있습니다. 최고 품질의 손떨림 보정 또는 기타 처리가 가능합니다.

  • VIDEO_CALL: 전력 소모가 큰 카메라 장시간 실행에 권장됩니다. 걱정할 필요가 없습니다

  • PREVIEW_VIDEO_STILL: 소셜 미디어 앱 또는 단일 스트림 사용에 권장 있습니다. 다목적 스트림입니다.

  • VENDOR_START: OEM에서 정의한 사용 사례에 사용됩니다.

CameraCaptureSession 만들기

카메라 세션을 만들려면 하나 이상의 출력 버퍼를 제공하세요. 출력 프레임을 작성할 수 있습니다. 각 버퍼는 파이프라인을 나타냅니다. 다음을 수행해야 합니다. 카메라를 사용하기 전에 이 작업을 수행해야 기기의 내부 파이프라인에 액세스하고 프레임 전송을 위한 메모리 버퍼를 할당합니다. 전달할 수 있습니다

다음 코드 스니펫은 두 개의 인코더-디코더에 속하는 SurfaceView 및 다른 하나는 ImageReader previewSurfacePREVIEW 스트림 사용 사례 추가 및 STILL_CAPTURE 스트림 사용 케이스를 imReaderSurface로 처리하면 기기 하드웨어가 이러한 스트림을 최적화할 수도 있습니다. 더 있습니다.

Kotlin


// Retrieve the target surfaces, which might be coming from a number of places:
// 1. SurfaceView, if you want to display the image directly to the user
// 2. ImageReader, if you want to read each frame or perform frame-by-frame
// analysis
// 3. OpenGL Texture or TextureView, although discouraged for maintainability
      reasons
// 4. RenderScript.Allocation, if you want to do parallel processing
val surfaceView = findViewById<SurfaceView>(...)
val imageReader = ImageReader.newInstance(...)

// Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated()
val previewSurface = surfaceView.holder.surface
val imReaderSurface = imageReader.surface
val targets = listOf(previewSurface, imReaderSurface)

// Create a capture session using the predefined targets; this also involves
// defining the session state callback to be notified of when the session is
// ready
// Setup Stream Use Case while setting up your Output Configuration.
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
fun configureSession(device: CameraDevice, targets: List<Surface>){
    val configs = mutableListOf<OutputConfiguration>()
    val streamUseCase = CameraMetadata
        .SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL

    targets.forEach {
        val config = OutputConfiguration(it)
        config.streamUseCase = streamUseCase.toLong()
        configs.add(config)
    }
    ...
    device.createCaptureSession(session)
}

자바


// Retrieve the target surfaces, which might be coming from a number of places:
// 1. SurfaceView, if you want to display the image directly to the user
// 2. ImageReader, if you want to read each frame or perform frame-by-frame
      analysis
// 3. RenderScript.Allocation, if you want to do parallel processing
// 4. OpenGL Texture or TextureView, although discouraged for maintainability
      reasons
Surface surfaceView = findViewById<SurfaceView>(...);
ImageReader imageReader = ImageReader.newInstance(...);

// Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated()
Surface previewSurface = surfaceView.getHolder().getSurface();
Surface imageSurface = imageReader.getSurface();
List<Surface> targets = Arrays.asList(previewSurface, imageSurface);

// Create a capture session using the predefined targets; this also involves defining the
// session state callback to be notified of when the session is ready
private void configureSession(CameraDevice device, List<Surface> targets){
    ArrayList<OutputConfiguration> configs= new ArrayList()
    String streamUseCase=  CameraMetadata
        .SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL

    for(Surface s : targets){
        OutputConfiguration config = new OutputConfiguration(s)
        config.setStreamUseCase(String.toLong(streamUseCase))
        configs.add(config)
}

device.createCaptureSession(session)
}

이 시점에서는 카메라의 활성 구성을 정의하지 않았습니다. 세션이 구성되면 캡처를 만들고 전달할 수 있습니다. 요청할 수 있습니다

버퍼에 작성될 때 입력에 적용되는 변환은 각 타겟의 유형에 따라 결정되며 Surface Android 프레임워크는 활성 구성의 원시 이미지를 확인할 수 있습니다 변환은 픽셀 형식 및 특정 Surface

프레임워크는 최선을 다하려고 하지만 일부 Surface가 있습니다. 구성 조합이 작동하지 않아 세션과 같은 문제를 생성되지 않았거나, 요청을 전달할 때 런타임 오류가 발생한 경우, 또는 성능이 저하될 수 있습니다 프레임워크는 데이터 레이크의 특정 노출 영역, 요청 매개변수의 조합으로 설정할 수 있습니다 이 createCaptureSession() 드림 를 참조하세요.

단일 CaptureRequest

각 프레임에 사용되는 구성은 CaptureRequest로 인코딩됩니다. 카메라로 전송됩니다. 캡처 요청을 만들려면 사전 정의된 템플릿, 또는 TEMPLATE_MANUAL를 사용하여 완전히 제어할 수 있습니다. 배포를 선택할 때 템플릿에서 사용할 수 있는 하나 이상의 출력 버퍼를 요청을 처리합니다 캡처에 이미 정의된 버퍼만 사용할 수 있습니다. 세션을 정의합니다.

캡처 요청은 빌더 패턴 개발자에게 다양한 옵션을 설정할 수 있는 기회를 옵션에는 다음과 같은 옵션이 포함됩니다. 자동 노출, 자동 초점, 및 렌즈 조리개. 필드를 설정하기 전에 를 호출하여 기기를 CameraCharacteristics.getAvailableCaptureRequestKeys() 드림 적절한 카메라를 확인하여 원하는 값이 지원되는지 특성(예: 사용 가능한 자동 노출) 또는 모드를 참고하세요.

템플릿을 사용하여 SurfaceView의 캡처 요청을 만들려면 다음 안내를 따르세요. 수정 없이 미리보기용으로 설계된 경우 CameraDevice.TEMPLATE_PREVIEW:

Kotlin

val session: CameraCaptureSession = ...  // from CameraCaptureSession.StateCallback
val captureRequest = session.device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
captureRequest.addTarget(previewSurface)

자바

CameraCaptureSession session = ...;  // from CameraCaptureSession.StateCallback
CaptureRequest.Builder captureRequest =
    session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
captureRequest.addTarget(previewSurface);

캡처 요청이 정의되면 이제 다음을 디스패치할 수 있습니다. 카메라 세션에 전송합니다.

Kotlin

val session: CameraCaptureSession = ...  // from CameraCaptureSession.StateCallback
val captureRequest: CaptureRequest = ...  // from CameraDevice.createCaptureRequest()

// The first null argument corresponds to the capture callback, which you
// provide if you want to retrieve frame metadata or keep track of failed capture
// requests that can indicate dropped frames; the second null argument
// corresponds to the Handler used by the asynchronous callback, which falls
// back to the current thread's looper if null
session.capture(captureRequest.build(), null, null)

자바

CameraCaptureSession session = ...;  // from CameraCaptureSession.StateCallback
CaptureRequest captureRequest = ...;  // from CameraDevice.createCaptureRequest()

// The first null argument corresponds to the capture callback, which you
// provide if you want to retrieve frame metadata or keep track of failed
// capture
// requests that can indicate dropped frames; the second null argument
// corresponds to the Handler used by the asynchronous callback, which falls
// back to the current thread's looper if null
session.capture(captureRequest.build(), null, null);

출력 프레임이 특정 버퍼에 배치되면, 콜백 시작됩니다 대부분의 경우 ImageReader.OnImageAvailableListener님, 는 포함된 프레임이 처리될 때 트리거됩니다. 현재 위치: 지정된 버퍼에서 이미지 데이터를 가져올 수 있습니다.

CaptureRequest 반복

단일 카메라 요청은 간단하지만 그다지 유용하지 않습니다. 이 경우 연속적인 프레임 스트림을 제공해야 합니다. 다음 코드 스니펫 여기에 반복 요청 있습니다.

Kotlin

val session: CameraCaptureSession = ...  // from CameraCaptureSession.StateCallback
val captureRequest: CaptureRequest = ...  // from CameraDevice.createCaptureRequest()

// This keeps sending the capture request as frequently as possible until
// the
// session is torn down or session.stopRepeating() is called
// session.setRepeatingRequest(captureRequest.build(), null, null)

자바

CameraCaptureSession session = ...;  // from CameraCaptureSession.StateCallback
CaptureRequest captureRequest = ...;  // from CameraDevice.createCaptureRequest()

// This keeps sending the capture request as frequently as possible until the
// session is torn down or session.stopRepeating() is called
// session.setRepeatingRequest(captureRequest.build(), null, null);

캡처를 반복 요청하면 카메라 기기가 지속적으로 캡처합니다. 제공된 CaptureRequest의 설정을 사용하여 이미지를 삭제할 수 있습니다. Camera2 API 을 사용하면 사용자가 카메라에서 동영상을 캡처할 수 있습니다. 이 그림에 표시된 대로 CaptureRequests 반복 Camera2 샘플 찾을 수 있습니다. 또한 피사체를 캡처하여 슬로 모션 동영상을 렌더링할 수도 있습니다. 반복 버스트 CaptureRequests를 사용한 고속 (슬로 모션) 동영상 Camera2 슬로 모션 동영상 샘플 앱에 표시된 대로 를 참조하세요.

CaptureRequest 인터리브

반복 캡처 요청이 활성화된 상태에서 두 번째 캡처 요청을 보내려면 예를 들어 뷰파인더를 표시하고 사용자가 사진을 캡처할 수 있도록 하려면 진행 중인 반복 요청을 중지합니다. 대신 반복되지 않는 캡처를 실행합니다. 반복 요청이 계속 실행되는 동안 이 요청을 다시 반복할 수 있습니다.

사용된 모든 출력 버퍼는 카메라 세션의 일부로 구성해야 합니다. 세션이 처음 생성될 때 반복 요청의 우선순위가 다음보다 낮습니다. 다음 샘플이 작동하도록 해 주는 단일 프레임 또는 버스트 요청입니다.

Kotlin

val session: CameraCaptureSession = ...  // from CameraCaptureSession.StateCallback

// Create the repeating request and dispatch it
val repeatingRequest = session.device.createCaptureRequest(
CameraDevice.TEMPLATE_PREVIEW)
repeatingRequest.addTarget(previewSurface)
session.setRepeatingRequest(repeatingRequest.build(), null, null)

// Some time later...

// Create the single request and dispatch it
// NOTE: This can disrupt the ongoing repeating request momentarily
val singleRequest = session.device.createCaptureRequest(
CameraDevice.TEMPLATE_STILL_CAPTURE)
singleRequest.addTarget(imReaderSurface)
session.capture(singleRequest.build(), null, null)

자바

CameraCaptureSession session = ...;  // from CameraCaptureSession.StateCallback

// Create the repeating request and dispatch it
CaptureRequest.Builder repeatingRequest =
session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
repeatingRequest.addTarget(previewSurface);
session.setRepeatingRequest(repeatingRequest.build(), null, null);

// Some time later...

// Create the single request and dispatch it
// NOTE: This can disrupt the ongoing repeating request momentarily
CaptureRequest.Builder singleRequest =
session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
singleRequest.addTarget(imReaderSurface);
session.capture(singleRequest.build(), null, null);

하지만 이 접근 방식에는 단점이 있습니다. 단일 요청이 발생합니다 다음 그림에서 A가 반복이면 B는 단일 프레임 캡처 요청이므로 세션에서 요청 대기열을 처리합니다.

그림 2. 진행 중인 카메라 세션의 요청 대기열 그림

다음에서 전송된 마지막 반복 요청 간의 지연 시간은 요청 B가 활성화되기 전 A와 다음에 A가 사용될 때 따라서 일부 건너뛴 프레임이 발생할 수 있습니다. 데이터 애널리스트가 사용할 수 있는 이 문제를 완화하기 위해 취할 수 있는 몇 가지 조치는 다음과 같습니다.

  • 요청 A의 출력 대상을 요청 B에 추가합니다. 그렇게 하면 B의 프레임이 준비되었으며 A의 출력 타겟에 복사되었습니다. 예를 들어, 이 작업은 스냅샷을 사용하여 동영상의 기존 상태를 유지하는 데 필수적입니다. 꾸준한 프레임 속도를 유지해야 합니다. 위 코드에서는 singleRequest.addTarget(previewSurface)를 호출합니다.

  • 이 특정 시나리오에 적합하도록 설계된 템플릿 조합을 사용하세요. 예를 들면 셔터 랙 제로에 있습니다