Not: Bu sayfa Kamera2 paketiyle ilgilidir. Uygulamanız için Kamera2'nin belirli, alt düzey özellikleri gerekmiyorsa, KameraX'i kullanmanızı öneririz. Hem CameraX hem de Camera2, Android 5.0 (API düzeyi 21) ve sonraki sürümleri destekler.
Bir kamera uygulaması aynı anda birden fazla kare akışı kullanabilir. İçinde Hatta bazı durumlarda farklı akışlar için farklı bir kare çözünürlüğü veya biçimindedir. Bazı tipik kullanım alanları şunlardır:
- Video kaydı: Önizleme için bir akış, diğeri kodlanıp kaydedilir bir dosyaya koyabilirsiniz.
- Barkod tarama: Önizleme için bir akış, barkod algılama için başka bir akış.
- Bilişimsel fotoğrafçılık: Önizleme için bir akış, yüz/sahne için başka bir akış tespit edebilir.
Çerçeveleri işlerken önemsiz olmayan bir performans maliyeti vardır ve bunun maliyeti artar.
CPU, GPU ve TTP gibi kaynaklar çerçevenin yeniden işlemesi ancak bellek gibi kaynaklar doğrusal olarak büyüyecektir.
İstek başına birden fazla hedef
Birden fazla kamera akışı tek bir cihazda birleştirilebilir
CameraCaptureRequest
.
Aşağıdaki kod snippet'i, tek bir hesapla kamera oturumu
kamera önizlemesi için akış ve resim işleme için başka bir akış:
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)
Java
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);
Hedef yüzeyleri doğru şekilde yapılandırırsanız bu kod yalnızca
tarafından belirlenen minimum FPS'yi karşılayan akışlar
StreamComfigurationMap.GetOutputMinFrameDuration(int, Size)
ve
StreamComfigurationMap.GetOutputStallDuration(int, Size)
.
Gerçek performans cihazdan cihaza değişebilir, ancak Android bazı
Üç değişkene bağlı olarak belirli kombinasyonları destekleme garantileri:
çıktı türünü, çıktı boyutunu ve donanım düzeyini kontrol edin.
Desteklenmeyen değişken kombinasyonu kullanmak düşük kare hızında işe yarayabilir. eğer
çalışmazsa başarısız geri çağırmalardan birini tetikler.
createCaptureSession
ile ilgili dokümanlar
neyin garanti edildiğini açıklar.
Çıkış türü
Çıkış türü, karelerin kodlandığı biçimi belirtir. İlgili içeriği oluşturmak için kullanılan
olası değerler PRIV, YUV, JPEG ve RAW'dur. Dokümanlar
createCaptureSession
tanımladığını gösterir.
Uygulamanızın çıkış türünü seçerken amacınız
kullanıyorsanız
ImageFormat.YUV_420_888
çerçeve analizi ve
Hareketsiz için ImageFormat.JPEG
resim. Önizleme ve kayıt senaryoları için muhtemelen bir
SurfaceView
TextureView
,
MediaRecorder
MediaCodec
veya
RenderScript.Allocation
. İçinde
resim biçimi belirtmeyin. Uyumluluk açısından,
ImageFormat.PRIVATE
dahili olarak kullanılan gerçek biçimden bağımsız olarak. Desteklenen biçimleri sorgulamak için
cihaz türüne göre
CameraCharacteristics
şu kodu kullanın:
Kotlin
val characteristics: CameraCharacteristics = ... val supportedFormats = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).outputFormats
Java
CameraCharacteristics characteristics = …; int[] supportedFormats = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputFormats();
Çıkış boyutu
Kullanılabilen tüm çıktı boyutları, aşağıdaki ölçütlere göre listelenmiştir:
StreamConfigurationMap.getOutputSizes()
,
Ancak bunlardan yalnızca ikisi uyumlulukla ilgilidir: PREVIEW
ve MAXIMUM
. Boyutlar
üst sınırlar işlevi görür. PREVIEW
boyutundaki bir cihaz çalışıyorsa
PREVIEW
altındaki boyutlar da işe yarar. Aynı durum MAXIMUM
için de geçerlidir. İlgili içeriği oluşturmak için kullanılan
belgeleri
CameraDevice
açıklayacağım.
Kullanılabilir çıktı boyutları, biçim seçimine bağlıdır. Raporda
CameraCharacteristics
ve bir biçim kullanıyorsanız, kullanılabilir çıkış boyutlarını şu şekilde sorgulayabilirsiniz:
Kotlin
val characteristics: CameraCharacteristics = ... val outputFormat: Int = ... // such as ImageFormat.JPEG val sizes = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) .getOutputSizes(outputFormat)
Java
CameraCharacteristics characteristics = …; int outputFormat = …; // such as ImageFormat.JPEG Size[] sizes = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) .getOutputSizes(outputFormat);
Kamera önizlemesi ve kaydı kullanım alanlarında, hedef sınıfı kullanarak desteklenen boyutlar. Biçim, kamera çerçevesinin kendisi tarafından işlenir:
Kotlin
val characteristics: CameraCharacteristics = ... val targetClass: Class <T> = ... // such as SurfaceView::class.java val sizes = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) .getOutputSizes(targetClass)
Java
CameraCharacteristics characteristics = …; int outputFormat = …; // such as ImageFormat.JPEG Size[] sizes = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) .getOutputSizes(outputFormat);
MAXIMUM
boyutunu elde etmek için çıktı boyutlarını alana göre sıralayın ve en büyük olanı döndürün
bir:
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 } }
Java
@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
, cihazın ekran çözünürlüğüyle veya cihazın ekran çözünürlüğüyle en iyi
1080p (1920x1080) (hangisi daha küçükse). En boy oranı
en boy oranına uygun hale getirmek için sinemaskop veya
akışı kırparak tam ekran modunda görüntüleyebilirsiniz. Doğru
önizleme boyutunu oluşturabilirsiniz. Aynı zamanda, kullanılabilir çıktı boyutlarını görüntü boyutuyla
Ekranın döndürülebileceği de hesaba katılır.
Aşağıdaki kod, boyutu yapacak SmartSize
yardımcı sınıfını tanımlar
biraz daha kolay hale getiriyor:
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 }
Java
/** 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; }
Desteklenen donanım seviyesini kontrol edin
Çalışma zamanında kullanılabilir özellikleri belirlemek için desteklenen donanımı kontrol edin
düzeyi kullanın
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL
.
Şununla
CameraCharacteristics
nesnesini tanımlarsanız donanım düzeyini tek bir ifadeyle alabilirsiniz:
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)
Java
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);
Tüm parçaları bir araya getirmek
Çıkış türü, çıkış boyutu ve donanım düzeyi ile hangi ölçütlerin karşılanacağını belirlemek için
geçerli olduğundan emin olun. Aşağıdaki grafikte
desteklenen yapılandırmalara sahip CameraDevice
LEGACY
kullanabilirsiniz.
Hedef 1 | Hedef 2 | Hedef 3 | Örnek kullanım alanları | |||
---|---|---|---|---|---|---|
Tür | Maksimum boyut | Tür | Maksimum boyut | Tür | Maksimum boyut | |
PRIV |
MAXIMUM |
Basit önizleme, GPU video işleme veya önizleme olmayan video kaydı. | ||||
JPEG |
MAXIMUM |
Vizörsüz sabit görüntü yakalama. | ||||
YUV |
MAXIMUM |
Uygulama içi video/resim işleme. | ||||
PRIV |
PREVIEW |
JPEG |
MAXIMUM |
Standart görüntüleme işlemi devam ediyor. | ||
YUV |
PREVIEW |
JPEG |
MAXIMUM |
Uygulama içi işleme artı görüntü yakalama. | ||
PRIV |
PREVIEW |
PRIV |
PREVIEW |
Standart kayıt. | ||
PRIV |
PREVIEW |
YUV |
PREVIEW |
Önizleme ve uygulama içi işleme. | ||
PRIV |
PREVIEW |
YUV |
PREVIEW |
Önizleme ve uygulama içi işleme. | ||
PRIV |
PREVIEW |
YUV |
PREVIEW |
JPEG |
MAXIMUM |
Yakalamaya devam etme ve uygulama içi işleme. |
LEGACY
, mümkün olan en düşük donanım düzeyidir. Bu tablo, her bir
Kamera2'yi destekleyen bir cihaz (API düzeyi 21 ve üstü) en fazla üç çıkış yapabilir
doğru yapılandırmayı kullanarak
aynı anda birden fazla video oynatın.
ek yük sınırlayıcı performans (örneğin, bellek, CPU veya termal kısıtlamalar).
Uygulamanızın, hedefleme çıktı tamponlarını da yapılandırması gerekir. Örneğin,
LEGACY
donanım düzeyinde bir cihazı hedeflerseniz iki hedef çıkış ayarlayabilirsiniz
biri ImageFormat.PRIVATE
, diğeri ise
ImageFormat.YUV_420_888
. Bu kombinasyon,
PREVIEW
boyut. Bu konunun önceki bölümlerinde tanımlanan işlevi kullanarak,
kamera kimliği için gerekli önizleme boyutları için şu kod gereklidir:
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)
Java
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
sağlanan geri çağırmaları kullanarak hazır olana kadar beklemeniz gerekir:
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 } ... })
Java
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 } ... });
Şu ifadeyi çağırarak SurfaceView
cihazını kamera çıkışı boyutuyla eşleşmeye zorlayabilirsiniz:
SurfaceHolder.setFixedSize()
Şuna benzer bir yaklaşım benimseyebilirsiniz:
Common'tan AutoFitSurfaceView
modül
mutlak bir boyut belirleyen, GitHub'daki kamera örneklerinin sayısını
hem en boy oranını hem de kullanılabilir alanı hesaba katar
etkinlik değişikliklerinin ne zaman tetikleneceğini ayarlar.
Diğer yüzeyi
İstediğiniz biçime sahip ImageReader
daha kolay olur, çünkü beklenebilecek geri çağırmalar olmaz:
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)
Java
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
gibi bir engelleme hedefi arabelleği kullanırken şundan sonra kareleri silin:
nasıl kullanacağımızı konuştuk.
Kotlin
imageReader.setOnImageAvailableListener({ val frame = it.acquireNextImage() // Do something with "frame" here it.close() }, null)
Java
imageReader.setOnImageAvailableListener(listener -> { Image frame = listener.acquireNextImage(); // Do something with "frame" here listener.close(); }, null);
LEGACY
donanım düzeyi, en düşük ortak paydaya sahip cihazları hedefler. Şunları yapabilirsiniz:
koşullu dallandırma ekleme ve çıkış hedeflerinin biri için RECORD
boyutu kullanma
LIMITED
donanım seviyesine sahip cihazlarda yüzeyleri keşfedebilir veya
Donanım seviyesinde FULL
olan cihazlar için MAXIMUM
boyut.