Kamerayla yakalama oturumları ve istekleri

Not: Bu sayfa, Kamera2 paketiyle ilgilidir. Uygulamanız Camera2'nin belirli, alt düzey özelliklerini gerektirmiyorsa KameraX'i kullanmanızı öneririz. Hem CameraX hem de Camera2, Android 5.0 (API düzeyi 21) ve sonraki sürümleri destekler.

Android destekli tek bir cihazda birden fazla kamera olabilir. Her kamera CameraDevice şeklindedir ve CameraDevice aynı anda birden fazla yayın çıkışı yapabilir.

Bunu yapmanın bir nedeni, CameraDevice kaynağından gelen ardışık kamera kareleri olan bir akışın belirli bir görev (ör. vizör görüntülemek) için optimize edilmesidir. Diğerleri ise fotoğraf çekmek veya video kaydı yapmak için kullanılabilir. Akışlar, her defasında bir kare olmak üzere kameradan çıkan ham kareleri işleyen paralel ardışık düzenler olarak çalışır:

Şekil 1. Evrensel Kamera Uygulaması Oluşturma konulu çizim (Google I/O '18)

Paralel işleme, CPU, GPU veya başka bir işlemciden gelen mevcut işlem gücüne bağlı olarak performans sınırları olabileceğini gösterir. Bir ardışık düzen gelen karelere yetişemezse bunları atmaya başlar.

Her ardışık düzenin kendi çıkış biçimi vardır. Gelen ham veriler, her bir ardışık düzenle ilişkilendirilen dolaylı mantık tarafından otomatik olarak uygun çıkış biçimine dönüştürülür. Bu sayfanın kod örneklerinde kullanılan CameraDevice spesifik değildir. Bu nedenle, devam etmeden önce mevcut tüm kameraları numaralandırmanız gerekir.

Söz konusu CameraDevice öğesine özgü bir CameraCaptureSession oluşturmak için CameraDevice öğesini kullanabilirsiniz. CameraDevice, CameraCaptureSession kullanan her ham kare için bir çerçeve yapılandırması almalıdır. Yapılandırma; otomatik odaklama, diyafram açıklığı, efektler ve pozlama gibi kamera özelliklerini belirtir. Donanım kısıtlamaları nedeniyle, herhangi bir zamanda kamera sensöründe yalnızca tek bir yapılandırma aktif olur. Buna etkin yapılandırma denir.

Ancak akış kullanım alanları, çekim oturumlarını canlı yayınlamak için CameraDevice kullanmanın önceki yollarını iyileştirir ve genişletir. Bu sayede, kamera akışını belirli kullanım alanınıza göre optimize edebilirsiniz. Örneğin, görüntülü görüşmeleri optimize ederken pil ömrünü iyileştirebilir.

CameraCaptureSession, CameraDevice öğesine bağlı tüm olası ardışık düzenleri açıklar. Bir oturum oluşturulduğunda, ardışık düzenler ekleyemez veya kaldıramazsınız. CameraCaptureSession, CaptureRequest'lerden oluşan bir sıra tutar ve bunlar etkin yapılandırma haline gelir.

CaptureRequest, sıraya bir yapılandırma ekler ve CameraDevice ürününden çerçeve alması için mevcut ardışık düzenlerden birini, birkaçını veya tümünü seçer. Bir yakalama oturumunun süresi boyunca çok sayıda yakalama isteği gönderebilirsiniz. Her istek, etkin yapılandırmayı ve ham görüntüyü alan çıkış ardışık düzenleri kümesini değiştirebilir.

Daha iyi performans için Akış Kullanım Alanları'nı kullanın

Akış Kullanım Alanları, Camera2 yakalama oturumlarının performansını iyileştirmenin bir yoludur. Bunlar, parametreleri ayarlamak için donanım cihazına daha fazla bilgi verir. Böylece, belirli bir görev için daha iyi bir kamera deneyimi sağlanır.

Bu sayede kamera cihazı, her bir akış için kullanıcı senaryolarına göre kamera donanımı ve yazılım ardışık düzenlerini optimize edebilir. Akış Kullanım Alanları hakkında daha fazla bilgi için bkz. setStreamUseCase.

Akış Kullanım Alanları, CameraDevice.createCaptureRequest() içinde bir şablon ayarlamaya ek olarak belirli bir kamera akışının nasıl kullanıldığını daha ayrıntılı olarak belirtmenizi sağlar. Bu sayede kamera donanımı, belirli kullanım alanlarına uygun kalite veya gecikme değerlerine göre ince ayar, sensör modu veya kamera sensörü ayarları gibi parametreleri optimize edebilir.

Akış kullanım alanları aşağıdakileri içerir:

  • DEFAULT: Mevcut tüm uygulama davranışını kapsar. Herhangi bir Akış Kullanım Alanı ayarlamamakla eşdeğerdir.

  • PREVIEW: Vizör veya uygulama içi resim analizi için önerilir.

  • STILL_CAPTURE: Yüksek kaliteli yüksek çözünürlüklü çekim için optimize edilmiştir ve önizleme benzeri kare hızlarının korunması beklenmiyor.

  • VIDEO_RECORD: Cihaz tarafından destekleniyor ve uygulama tarafından etkinleştirildiyse yüksek kaliteli görüntü sabitleme dahil olmak üzere yüksek kaliteli video çekimleri için optimize edilmiştir. Bu seçenek, en yüksek kalitede sabitleme veya diğer işleme sağlamak için gerçek zamanlıdan önemli bir gecikmeli çıkış kareleri üretebilir.

  • VIDEO_CALL: Güç kaybının sorunlu olduğu uzun süreli kamera kullanımları için önerilir.

  • PREVIEW_VIDEO_STILL: Sosyal medya uygulamaları veya tek akış kullanım alanları için önerilir. Bu çok amaçlı bir akış.

  • VENDOR_START: OEM tarafından tanımlanan kullanım alanları için kullanılır.

CameraCaptureSession oluşturma

Kamera oturumu oluşturmak için, uygulamanızın çıkış karesi yazabileceği bir veya daha fazla çıkış arabelleği sağlayın. Her tampon bir ardışık düzeni temsil eder. Bunu kamerayı kullanmaya başlamadan önce yapmanız gerekir. Böylece çerçeve, cihazın dahili ardışık düzenlerini yapılandırabilir ve kareleri gereken çıkış hedeflerine göndermek için bellek arabellekleri ayırabilir.

Aşağıdaki kod snippet'inde, biri SurfaceView ve diğeri ImageReader'e ait olan iki çıkış arabelleğiyle nasıl bir kamera oturumu hazırlayabileceğiniz gösterilmektedir. previewSurface için PREVIEW Akış Kullanım Alanı ve STILL_CAPTURE Akış Kullanım Alanı'nın imReaderSurface içine eklenmesi, cihaz donanımının bu akışları daha da iyi optimize etmesine olanak tanır.

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)
}

Java


// 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)
}

Bu noktada, kameranın etkin yapılandırmasını tanımlamadınız. Oturum yapılandırıldığında, bunu yapmak için yakalama istekleri oluşturup gönderebilirsiniz.

Tamponlarına yazılırken girişlere uygulanan dönüşüm, her bir hedefin türüne göre belirlenir. Bu değer bir Surface olmalıdır. Android çerçevesi, etkin yapılandırmadaki ham bir görüntüyü her bir hedefe uygun bir biçime nasıl dönüştüreceğini bilir. Dönüştürme, belirli Surface öğesinin piksel biçimi ve boyutu ile kontrol edilir.

Çerçeve, elinden gelenin en iyisini yapmaya çalışır ancak bazı Surface yapılandırma kombinasyonları çalışmayabilir. Bu durum, oturumun oluşturulmaması, bir isteği gönderdiğinizde çalışma zamanı hatasının verilmesi veya performansın düşmesi gibi sorunlara neden olabilir. Bu çerçeve belirli cihaz, yüzey ve istek parametrelerinin kombinasyonları için garantiler sağlar. createCaptureSession() dokümanlarında daha fazla bilgi bulabilirsiniz.

Tek Yakalama İstekleri

Her kare için kullanılan yapılandırma, kameraya gönderilen bir CaptureRequest olarak kodlanır. Yakalama isteği oluşturmak için önceden tanımlanmış şablonlardan birini veya tam kontrol için TEMPLATE_MANUAL kullanabilirsiniz. Şablon seçtiğinizde istekle birlikte kullanılacak bir veya daha fazla çıkış arabelleği sağlamanız gerekir. Yalnızca kullanmayı düşündüğünüz yakalama oturumunda tanımlanmış arabellekleri kullanabilirsiniz.

Yakalama istekleri oluşturucu kalıbı kullanır ve geliştiricilere otomatik pozlama, otomatik odaklama ve lens diyafram dahil olmak üzere birçok farklı seçeneği ayarlama fırsatı sunar. Alan ayarlamadan önce, CameraCharacteristics.getAvailableCaptureRequestKeys() numaralı telefonu arayarak belirli bir seçeneğin kullanılabilir olduğundan ve kullanılabilir otomatik pozlama modları gibi uygun kamera özelliklerini kontrol ederek istenilen değerin desteklendiğinden emin olun.

Önizleme amacıyla tasarlanan şablonu, herhangi bir değişiklik yapmadan SurfaceView için yakalama isteği oluşturmak istiyorsanız CameraDevice.TEMPLATE_PREVIEW işlevini kullanın:

Kotlin

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

Java

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

Yakalama isteği tanımlandıktan sonra bu isteği kamera oturumuna gönderebilirsiniz:

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)

Java

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);

Çıkış çerçevesi belirli bir arabelleğe yerleştirildiğinde, yakalama geri çağırma tetiklenir. Çoğu durumda, içerdiği çerçeve işlendiğinde ImageReader.OnImageAvailableListener gibi ek geri çağırmalar tetiklenir. Bu noktada, resim verilerini belirtilen arabelleğin içinden alabilirsiniz.

Yinelenen Yakalama İstekleri

Tek kamera istekleri basit bir şekilde yapılabilir, ancak canlı önizleme veya video görüntülemek için pek faydalı olmaz. Bu durumda, yalnızca bir kare değil, sürekli bir kare akışı almanız gerekir. Aşağıdaki kod snippet'inde oturuma tekrarlanan isteğin nasıl ekleneceği gösterilmektedir:

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)

Java

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);

Tekrarlanan çekim isteği, kamera cihazının sağlanan CaptureRequest içindeki ayarları kullanarak sürekli olarak görüntü yakalamasını sağlar. Camera2 API, kullanıcıların GitHub'daki Kamera2 örnek deposunda gösterildiği gibi yinelenen CaptureRequests göndererek kameradan video çekmesini de sağlar. GitHub'daki Kamera2 ağır çekim video örnek uygulamasında gösterildiği gibi, tekrar eden seri çekim CaptureRequests özelliğini kullanarak yüksek hızlı (yavaş çekim) videolar çekerek de ağır çekim video oluşturabilir.

Aralık Yakalama İstekleri

Tekrarlanan çekim isteği etkinken ikinci bir çekim isteği göndermek (ör. vizör görüntülemek ve kullanıcıların fotoğraf çekmesine izin vermek) için yinelenen isteği durdurmanız gerekmez. Bunun yerine, tekrarlanan istek çalışmaya devam ederken tekrarlanmayan bir yakalama isteği gönderirsiniz.

Kullanılan çıkış arabelleği, oturum ilk oluşturulduğunda kamera oturumunun bir parçası olarak yapılandırılmalıdır. Tekrarlanan istekler, tek kare veya seri isteklerden daha düşük önceliğe sahiptir, bu da aşağıdaki örneğin çalışmasını sağlar:

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)

Java

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);

Ancak bu yaklaşımın bir dezavantajı vardır: Tek isteğin tam olarak ne zaman gerçekleştiğini bilemezsiniz. Aşağıdaki şekilde, A tekrarlanan yakalama isteği ve B tek kare yakalama isteğiyse oturum istek sırasını şu şekilde işler:

Şekil 2. Devam eden kamera oturumu için istek sırasını gösteren görsel

A isteği etkinleştirilmeden önce A'dan gelen son istek ile A'nın tekrar kullanılması arasındaki gecikmenin garantisi yoktur. Bu nedenle bazı kare atlamalarıyla karşılaşabilirsiniz. Bu sorunu azaltmak için yapabileceğiniz bazı şeyler vardır:

  • A isteğindeki çıkış hedeflerini B isteğine ekleyin. Bu şekilde, B'nin çerçevesi hazır olduğunda A'nın çıkış hedeflerine kopyalanır. Örneğin, video anlık görüntülerini çekerken sabit bir kare hızı sağlamak için bu gereklidir. Önceki kodda, isteği oluşturmadan önce singleRequest.addTarget(previewSurface) eklersiniz.

  • Bu özel senaryoda çalışmak üzere tasarlanmış, sıfır gecikme süresi gibi şablonların bir kombinasyonunu kullanın.