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ış:
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);
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:
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();
Çı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:
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);
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:
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
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:
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
, 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:
/** 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;
}
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:
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);
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:
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
sağlanan geri çağırmaları kullanarak hazır olana kadar beklemeniz gerekir:
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
}
...
});
Ş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:
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
gibi bir engelleme hedefi arabelleği kullanırken şundan sonra kareleri silin:
nasıl kullanacağımızı konuştuk.
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
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.