참고: 이 페이지에서는 Camera2 패키지를 다룹니다. 앱에 Camera2의 특정 하위 수준 기능이 필요하지 않다면 CameraX를 사용하는 것이 좋습니다. CameraX와 Camera2는 모두 Android 5.0(API 수준 21) 이상을 지원합니다.
카메라 애플리케이션은 두 개 이상의 프레임 스트림을 동시에 사용할 수 있습니다. 포함 스트림마다 다른 프레임 해상도나 픽셀이 필요한 경우도 있습니다. 형식으로 입력합니다. 몇 가지 일반적인 사용 사례는 다음과 같습니다.
- 동영상 녹화: 미리보기용 스트림 1개, 인코딩 및 저장용 스트림 1개 할 수 있습니다.
- 바코드 스캔: 미리보기용 스트림, 바코드 감지용 스트림
- 컴퓨팅 사진: 미리보기용 스트림, 얼굴/장면 스트림 1개 있습니다
프레임을 처리할 때 적지 않은 성능 비용이 발생하며 곱하기만 하면 됩니다.
CPU, GPU 및 DSP와 같은 리소스는 프레임워크의 재처리 메모리와 같은 리소스는 선형적으로 증가합니다.
요청당 여러 대상
여러 카메라 스트림을 하나로 결합할 수 있습니다.
CameraCaptureRequest
다음 코드 스니펫은 세션 하나를 사용하여 카메라 세션을 설정하는 방법을
카메라 미리보기를 위한 스트림과 이미지 처리를 위한 다른 스트림:
Kotlin
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback // You will use the preview capture template for the combined streams // because it is optimized for low latency; for high-quality images, use // TEMPLATE_STILL_CAPTURE, and for a steady frame rate use TEMPLATE_RECORD val requestTemplate = CameraDevice.TEMPLATE_PREVIEW val combinedRequest = session.device.createCaptureRequest(requestTemplate) // Link the Surface targets with the combined request combinedRequest.addTarget(previewSurface) combinedRequest.addTarget(imReaderSurface) // In this simple case, the SurfaceView gets updated automatically. ImageReader // has its own callback that you have to listen to in order to retrieve the // frames so there is no need to set up a callback for the capture request session.setRepeatingRequest(combinedRequest.build(), null, null)
자바
CameraCaptureSession session = …; // from CameraCaptureSession.StateCallback // You will use the preview capture template for the combined streams // because it is optimized for low latency; for high-quality images, use // TEMPLATE_STILL_CAPTURE, and for a steady frame rate use TEMPLATE_RECORD CaptureRequest.Builder combinedRequest = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); // Link the Surface targets with the combined request combinedRequest.addTarget(previewSurface); combinedRequest.addTarget(imReaderSurface); // In this simple case, the SurfaceView gets updated automatically. ImageReader // has its own callback that you have to listen to in order to retrieve the // frames so there is no need to set up a callback for the capture request session.setRepeatingRequest(combinedRequest.build(), null, null);
타겟 표면을 올바르게 구성하면 이 코드는
최소 FPS를 충족하는 스트림은
StreamComfigurationMap.GetOutputMinFrameDuration(int, Size)
드림
및
StreamComfigurationMap.GetOutputStallDuration(int, Size)
Android는 일부 기능을 제공하지만 실제 성능은 기기에 따라 다릅니다.
다음 세 가지 변수에 따라 특정 조합을 지원합니다.
출력 유형, 출력 크기, 하드웨어 수준이 있습니다.
지원되지 않는 변수 조합을 사용하면 낮은 프레임 속도로 작동할 수 있습니다. 조건:
트리거되지 않으면 실패 콜백 중 하나를 트리거합니다.
createCaptureSession
문서
무엇인지 설명합니다.
출력 유형
출력 유형은 프레임이 인코딩되는 형식을 나타냅니다. 이
가능한 값은 PRIV, YUV, JPEG 및 RAW입니다. 이
createCaptureSession
드림
설명합니다.
애플리케이션의 출력 유형을 선택할 때
지원하려면
ImageFormat.YUV_420_888
드림
프레임 분석 및
스틸 이미지의 경우 ImageFormat.JPEG
이미지 미리보기 및 녹화 시나리오의 경우
SurfaceView
님,
TextureView
님,
MediaRecorder
님,
MediaCodec
또는
RenderScript.Allocation
포함
이러한 경우 이미지 형식을 지정하지 마세요. 호환성을 위해
ImageFormat.PRIVATE
님,
내부에서 사용된 실제 형식과
상관이 없습니다 지원되는 형식 쿼리
주어진 조건에 따라
CameraCharacteristics
님,
다음 코드를 사용합니다.
Kotlin
val characteristics: CameraCharacteristics = ... val supportedFormats = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).outputFormats
자바
CameraCharacteristics characteristics = …; int[] supportedFormats = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputFormats();
출력 크기
사용 가능한 모든 출력 크기는
StreamConfigurationMap.getOutputSizes()
님,
호환성과 관련된 것은 PREVIEW
, MAXIMUM
뿐입니다. 크기
상한 역할을 합니다. PREVIEW
크기의 무언가가 유효하다면
PREVIEW
보다 작은 크기도 사용할 수 있습니다. MAXIMUM
의 경우도 마찬가지입니다. 이
문서
CameraDevice
드림
참조하세요.
사용 가능한 출력 크기는 선택한 형식에 따라 다릅니다. 주어진
CameraCharacteristics
드림
다음과 같이 사용 가능한 출력 크기를 쿼리할 수 있습니다.
Kotlin
val characteristics: CameraCharacteristics = ... val outputFormat: Int = ... // such as ImageFormat.JPEG val sizes = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) .getOutputSizes(outputFormat)
자바
CameraCharacteristics characteristics = …; int outputFormat = …; // such as ImageFormat.JPEG Size[] sizes = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) .getOutputSizes(outputFormat);
카메라 미리보기 및 녹화 사용 사례에서는 대상 클래스를 사용하여 다음을 결정합니다. 크기가 지원됩니다. 형식은 카메라 프레임워크 자체에서 처리됩니다.
Kotlin
val characteristics: CameraCharacteristics = ... val targetClass: Class <T> = ... // such as SurfaceView::class.java val sizes = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) .getOutputSizes(targetClass)
자바
CameraCharacteristics characteristics = …; int outputFormat = …; // such as ImageFormat.JPEG Size[] sizes = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) .getOutputSizes(outputFormat);
MAXIMUM
크기를 가져오려면 출력 크기를 영역으로 정렬하고 가장 큰 값을 반환합니다.
1개:
Kotlin
fun <T>getMaximumOutputSize( characteristics: CameraCharacteristics, targetClass: Class <T>, format: Int? = null): Size { val config = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) // If image format is provided, use it to determine supported sizes; or else use target class val allSizes = if (format == null) config.getOutputSizes(targetClass) else config.getOutputSizes(format) return allSizes.maxBy { it.height * it.width } }
자바
@RequiresApi(api = Build.VERSION_CODES.N) <T> Size getMaximumOutputSize(CameraCharacteristics characteristics, Class <T> targetClass, Integer format) { StreamConfigurationMap config = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); // If image format is provided, use it to determine supported sizes; else use target class Size[] allSizes; if (format == null) { allSizes = config.getOutputSizes(targetClass); } else { allSizes = config.getOutputSizes(format); } return Arrays.stream(allSizes).max(Comparator.comparing(s -> s.getHeight() * s.getWidth())).get(); }
PREVIEW
는 기기의 화면 해상도에 가장 잘 맞는 크기 또는
1080p (1920x1080) 중 작은 크기 가로세로 비율이
화면비율과 정확하게 일치하므로 레터박스 또는
전체 화면 모드로 표시하기 위해 스트림 자르기 올바른 정보를 얻으려면
미리보기 크기를 표시하고 사용 가능한 출력 크기를 디스플레이 크기와 비교하는 동안
디스플레이가 회전될 수 있다는 점을 고려합니다.
다음 코드는 크기를 조절하는 도우미 클래스 SmartSize
를 정의합니다.
더 쉽게 비교할 수 있습니다.
Kotlin
/** Helper class used to pre-compute shortest and longest sides of a [Size] */ class SmartSize(width: Int, height: Int) { var size = Size(width, height) var long = max(size.width, size.height) var short = min(size.width, size.height) override fun toString() = "SmartSize(${long}x${short})" } /** Standard High Definition size for pictures and video */ val SIZE_1080P: SmartSize = SmartSize(1920, 1080) /** Returns a [SmartSize] object for the given [Display] */ fun getDisplaySmartSize(display: Display): SmartSize { val outPoint = Point() display.getRealSize(outPoint) return SmartSize(outPoint.x, outPoint.y) } /** * Returns the largest available PREVIEW size. For more information, see: * https://d.android.com/reference/android/hardware/camera2/CameraDevice */ fun <T>getPreviewOutputSize( display: Display, characteristics: CameraCharacteristics, targetClass: Class <T>, format: Int? = null ): Size { // Find which is smaller: screen or 1080p val screenSize = getDisplaySmartSize(display) val hdScreen = screenSize.long >= SIZE_1080P.long || screenSize.short >= SIZE_1080P.short val maxSize = if (hdScreen) SIZE_1080P else screenSize // If image format is provided, use it to determine supported sizes; else use target class val config = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)!! if (format == null) assert(StreamConfigurationMap.isOutputSupportedFor(targetClass)) else assert(config.isOutputSupportedFor(format)) val allSizes = if (format == null) config.getOutputSizes(targetClass) else config.getOutputSizes(format) // Get available sizes and sort them by area from largest to smallest val validSizes = allSizes .sortedWith(compareBy { it.height * it.width }) .map { SmartSize(it.width, it.height) }.reversed() // Then, get the largest output size that is smaller or equal than our max size return validSizes.first { it.long <= maxSize.long && it.short <= maxSize.short }.size }
자바
/** Helper class used to pre-compute shortest and longest sides of a [Size] */ class SmartSize { Size size; double longSize; double shortSize; public SmartSize(Integer width, Integer height) { size = new Size(width, height); longSize = max(size.getWidth(), size.getHeight()); shortSize = min(size.getWidth(), size.getHeight()); } @Override public String toString() { return String.format("SmartSize(%sx%s)", longSize, shortSize); } } /** Standard High Definition size for pictures and video */ SmartSize SIZE_1080P = new SmartSize(1920, 1080); /** Returns a [SmartSize] object for the given [Display] */ SmartSize getDisplaySmartSize(Display display) { Point outPoint = new Point(); display.getRealSize(outPoint); return new SmartSize(outPoint.x, outPoint.y); } /** * Returns the largest available PREVIEW size. For more information, see: * https://d.android.com/reference/android/hardware/camera2/CameraDevice */ @RequiresApi(api = Build.VERSION_CODES.N) <T> Size getPreviewOutputSize( Display display, CameraCharacteristics characteristics, Class <T> targetClass, Integer format ){ // Find which is smaller: screen or 1080p SmartSize screenSize = getDisplaySmartSize(display); boolean hdScreen = screenSize.longSize >= SIZE_1080P.longSize || screenSize.shortSize >= SIZE_1080P.shortSize; SmartSize maxSize; if (hdScreen) { maxSize = SIZE_1080P; } else { maxSize = screenSize; } // If image format is provided, use it to determine supported sizes; else use target class StreamConfigurationMap config = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); if (format == null) assert(StreamConfigurationMap.isOutputSupportedFor(targetClass)); else assert(config.isOutputSupportedFor(format)); Size[] allSizes; if (format == null) { allSizes = config.getOutputSizes(targetClass); } else { allSizes = config.getOutputSizes(format); } // Get available sizes and sort them by area from largest to smallest List <Size> sortedSizes = Arrays.asList(allSizes); List <SmartSize> validSizes = sortedSizes.stream() .sorted(Comparator.comparing(s -> s.getHeight() * s.getWidth())) .map(s -> new SmartSize(s.getWidth(), s.getHeight())) .sorted(Collections.reverseOrder()).collect(Collectors.toList()); // Then, get the largest output size that is smaller or equal than our max size return validSizes.stream() .filter(s -> s.longSize <= maxSize.longSize && s.shortSize <= maxSize.shortSize) .findFirst().get().size; }
지원되는 하드웨어 수준 확인
런타임에 사용 가능한 기능을 확인하려면 지원되는 하드웨어를 확인하세요.
레벨
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL
이
CameraCharacteristics
드림
객체가 있으면 단일 명령문으로 하드웨어 수준을 검색할 수 있습니다.
Kotlin
val characteristics: CameraCharacteristics = ... // Hardware level will be one of: // - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY, // - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL, // - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, // - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, // - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3 val hardwareLevel = characteristics.get( CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)
자바
CameraCharacteristics characteristics = ...; // Hardware level will be one of: // - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY, // - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL, // - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED, // - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, // - CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3 Integer hardwareLevel = characteristics.get( CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
총정리
출력 유형, 출력 크기, 하드웨어 수준을 사용해
유효한 스트림 조합입니다. 다음 차트는 2014년 2월 1일 이후에
CameraDevice
에서 지원하는 구성
LEGACY
하드웨어 수준입니다
타겟 1 | 타겟 2 | 타겟 3 | 샘플 사용 사례 | |||
---|---|---|---|---|---|---|
유형 | 최대 크기 | 유형 | 최대 크기 | 유형 | 최대 크기 | |
PRIV |
MAXIMUM |
간단한 미리보기, GPU 동영상 처리 또는 미리보기 없는 동영상 녹화 | ||||
JPEG |
MAXIMUM |
뷰파인더가 없는 정지 이미지 캡처 | ||||
YUV |
MAXIMUM |
애플리케이션 내 동영상/이미지 처리 | ||||
PRIV |
PREVIEW |
JPEG |
MAXIMUM |
표준 정지 이미징입니다. | ||
YUV |
PREVIEW |
JPEG |
MAXIMUM |
인앱 처리와 스틸 캡처 | ||
PRIV |
PREVIEW |
PRIV |
PREVIEW |
표준 녹화. | ||
PRIV |
PREVIEW |
YUV |
PREVIEW |
미리보기 및 인앱 처리 | ||
PRIV |
PREVIEW |
YUV |
PREVIEW |
미리보기 및 인앱 처리 | ||
PRIV |
PREVIEW |
YUV |
PREVIEW |
JPEG |
MAXIMUM |
스틸 캡처 및 인앱 처리 |
LEGACY
은 가능한 가장 낮은 하드웨어 수준입니다. 이 표에서는
Camera2 (API 수준 21 이상)를 지원하는 기기는 최대 세 개의
여러 개의 동시 스트림을 스트리밍하려는 경우
성능 제한(예: 메모리, CPU, 열 제약조건)이 있습니다.
앱은 타겟팅 출력 버퍼도 구성해야 합니다. 예를 들어
하드웨어 수준이 LEGACY
인 기기를 타겟팅하는 경우 두 개의 타겟 출력을 설정할 수 있습니다.
하나는 ImageFormat.PRIVATE
를 사용하고 다른 하나는
ImageFormat.YUV_420_888
입니다. 이는
PREVIEW
크기. 이 주제의 앞부분에서 정의한 함수를 사용하여
카메라 ID의 필수 미리보기 크기에는 다음 코드가 필요합니다.
Kotlin
val characteristics: CameraCharacteristics = ... val context = this as Context // assuming you are inside of an activity val surfaceViewSize = getPreviewOutputSize( context, characteristics, SurfaceView::class.java) val imageReaderSize = getPreviewOutputSize( context, characteristics, ImageReader::class.java, format = ImageFormat.YUV_420_888)
자바
CameraCharacteristics characteristics = ...; Context context = this; // assuming you are inside of an activity Size surfaceViewSize = getPreviewOutputSize( context, characteristics, SurfaceView.class); Size imageReaderSize = getPreviewOutputSize( context, characteristics, ImageReader.class, format = ImageFormat.YUV_420_888);
제공된 콜백을 사용하여 SurfaceView
가 준비될 때까지 기다려야 합니다.
Kotlin
val surfaceView = findViewById <SurfaceView>(...) surfaceView.holder.addCallback(object : SurfaceHolder.Callback { override fun surfaceCreated(holder: SurfaceHolder) { // You do not need to specify image format, and it will be considered of type PRIV // Surface is now ready and you could use it as an output target for CameraSession } ... })
자바
SurfaceView surfaceView = findViewById <SurfaceView>(...); surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) { // You do not need to specify image format, and it will be considered of type PRIV // Surface is now ready and you could use it as an output target for CameraSession } ... });
다음을 호출하여 SurfaceView
를 카메라 출력 크기와 일치하도록 강제할 수 있습니다.
SurfaceHolder.setFixedSize()
kubectl 명령어와 유사한 방식으로
Commons의 AutoFitSurfaceView
모듈
절대 크기를 설정하는 카메라 샘플의 예입니다.
가로세로 비율과 사용 가능한 공간을 모두 고려하며,
활동 변경이 트리거되는 시점을 조정합니다.
다른 표시 경로를 설정하고
원하는 형식의 ImageReader
:
더 쉬울 수 있습니다. 이는 기다릴 콜백이 없기 때문입니다.
Kotlin
val frameBufferCount = 3 // just an example, depends on your usage of ImageReader val imageReader = ImageReader.newInstance( imageReaderSize.width, imageReaderSize.height, ImageFormat.YUV_420_888, frameBufferCount)
자바
int frameBufferCount = 3; // just an example, depends on your usage of ImageReader ImageReader imageReader = ImageReader.newInstance( imageReaderSize.width, imageReaderSize.height, ImageFormat.YUV_420_888, frameBufferCount);
ImageReader
와 같은 차단 타겟 버퍼를 사용할 때는
사용할 수 있습니다.
Kotlin
imageReader.setOnImageAvailableListener({ val frame = it.acquireNextImage() // Do something with "frame" here it.close() }, null)
자바
imageReader.setOnImageAvailableListener(listener -> { Image frame = listener.acquireNextImage(); // Do something with "frame" here listener.close(); }, null);
하드웨어 수준 LEGACY
개가 가장 낮은 공통분모 기기를 타겟팅합니다. 다음과 같은 작업을 할 수 있습니다.
조건부 브랜치를 추가하고 출력 타겟 중 하나에 RECORD
크기를 사용합니다.
하드웨어 수준이 LIMITED
인 기기에 노출 영역을 표시하거나
하드웨어 수준이 FULL
인 기기의 경우 MAXIMUM
크기입니다.