جلسات وجلسات تصوير للكاميرا

ملاحظة: تشير هذه الصفحة إلى حزمة camera2. ننصحك باستخدام cameraX ما لم يكن تطبيقك يتطلب ميزات محدّدة ومنخفضة المستوى من Camera2. يتوافق كل من CameraX و Camera2 مع الإصدار Android 5.0 (المستوى 21 من واجهة برمجة التطبيقات) والإصدارات الأحدث.

يمكن أن يتضمّن الجهاز الواحد الذي يعمل بنظام التشغيل Android عدة كاميرات. كل كاميرا هي عبارة عن CameraDevice، ويمكن لـ CameraDevice إخراج أكثر من بث واحد في الوقت نفسه.

وأحد الأسباب وراء ذلك هو تحسينCameraDevice

الشكل 1. رسم توضيحي من إنشاء تطبيق كاميرا عالمية (Google I/O ‘18)

تشير المعالجة المتوازية إلى أنه قد تكون هناك حدود للأداء بناءً على طاقة المعالجة المتاحة من وحدة المعالجة المركزية (CPU) أو وحدة معالجة الرسومات أو معالج آخر. إذا تعذّر على مسار المعالجة مواكبة الإطارات الواردة، فإنها تبدأ في إسقاطها.

لكل مسار تنسيق إخراج خاص به. يتم تلقائيًا تحويل البيانات الأولية الواردة إلى تنسيق الناتج المناسب من خلال المنطق الضمني المرتبط بكل مسار. ويُرجى العلم أنّ السمة CameraDevice المستخدَمة في نماذج التعليمات البرمجية لهذه الصفحة غير محدّدة، لذا يجب أولاً تعداد جميع الكاميرات المتاحة قبل المتابعة.

يمكنك استخدام السمة CameraDevice لإنشاء السمة CameraCaptureSession التي تخصّ CameraDevice. يجب أن يتلقّى CameraDevice إعدادات إطار لكل إطار غير معدَّل يستخدم CameraCaptureSession. تحدد الإعدادات سمات الكاميرا مثل التركيز التلقائي وفتحة العدسة والتأثيرات ودرجة الإضاءة. بسبب قيود الأجهزة، لا يتم تفعيل سوى عملية إعداد واحدة في أداة استشعار الكاميرا في أي وقت، وهذا ما يسمى بالإعداد النشط.

مع ذلك، تحسّن "حالات الاستخدام للبث" وتوسِّع نطاق الطرق السابقة لاستخدام CameraDevice لبث جلسات التصوير، ما يتيح لك تحسين بث الكاميرا لحالات الاستخدام الخاصة بك. على سبيل المثال، يمكنه تحسين عمر البطارية عند تحسين مكالمات الفيديو.

تصف السمة CameraCaptureSession جميع المسارات المحتمَلة المرتبطة بـ CameraDevice. عند إنشاء جلسة، لا يمكنك إضافة مسارات أو إزالتها. يحتفظ CameraCaptureSession بقائمة انتظار تتألف من CaptureRequest، والتي تصبح الإعدادات النشطة.

تضيف السمة CaptureRequest إعدادات إلى قائمة الانتظار وتختار مسارًا واحدًا أو أكثر أو كل المسارات المتاحة لتلقّي إطار من CameraDevice. يمكنك إرسال العديد من طلبات الالتقاط على مدار مدة جلسة الالتقاط. ويمكن لكل طلب تغيير الإعدادات النشطة ومجموعة مسارات الإخراج التي تتلقّى الصورة الأوّلية.

استخدام حالات الاستخدام للبث للحصول على أداء أفضل

تُعد "حالات الاستخدام للبث" طريقةً لتحسين أداء جلسات التقاط الصور من خلال Camera2. وتمنح الأجهزة للأجهزة مزيدًا من المعلومات لضبط المعلمات، مما يوفر تجربة كاميرا أفضل لمهمتك المحددة.

ويسمح هذا لجهاز الكاميرا بتحسين أجهزة الكاميرا ومسارات البرامج استنادًا إلى سيناريوهات المستخدمين لكل بث. لمزيد من المعلومات حول "حالات استخدام ساحة المشاركات"، يُرجى الاطّلاع على setStreamUseCase.

تتيح لك "حالات الاستخدام للبث" تحديد كيفية استخدام بث كاميرا معيّن بمزيد من التفصيل، بالإضافة إلى ضبط نموذج في CameraDevice.createCaptureRequest(). ويسمح ذلك لأجهزة الكاميرا بتحسين المعلمات، مثل التوليف، أو وضع أداة الاستشعار، أو إعدادات أداة استشعار الكاميرا، بناءً على مفاضلات الجودة أو وقت الاستجابة المناسبة لحالات استخدام معيّنة.

تشمل حالات الاستخدام للبث ما يلي:

  • DEFAULT: يشمل جميع السلوكيات الحالية للتطبيقات. يعادل ذلك عدم تعيين أي حالة استخدام للبث.

  • PREVIEW: يُنصح باستخدامها لأداة "عدسة الكاميرا" أو لتحليل الصور داخل التطبيق.

  • STILL_CAPTURE: تم تحسين هذه الميزة للحصول على لقطة عالية الجودة بدقة عالية، وليس من المتوقّع أن تحافظ على معدّل عرض الإطارات الذي يشبه المعاينة.

  • VIDEO_RECORD: تم تحسين هذه الميزة لالتقاط فيديوهات عالية الجودة، بما في ذلك تثبيت الصورة بجودة عالية، إذا كان الجهاز متوافقًا مع هذه الميزة وفعّله التطبيق. قد يُنتج هذا الخيار إطارات إخراج مع تأخّر كبير في الوقت الفعلي، للسماح بتحقيق أعلى جودة تثبيت أو معالجة أخرى.

  • VIDEO_CALL: يُنصح بهذا الخيار لاستخدام الكاميرا لفترات طويلة عندما يشكِّل استنزاف طاقة البطارية مصدر قلق.

  • PREVIEW_VIDEO_STILL: يوصى بها لتطبيقات وسائل التواصل الاجتماعي أو لحالات استخدام البث الفردي. بث مباشر متعدد الأغراض.

  • VENDOR_START: يُستخدم هذا الخيار لحالات الاستخدام التي يحدّدها المصنّع الأصلي للجهاز.

إنشاء جلسة CameraCaptureSession

لإنشاء جلسة كاميرا، وفّر لها مخازن إخراج واحدة أو أكثر يمكن لتطبيقك كتابة الإطارات الإخراجية عليها. يمثل كل مورد احتياطي مسارًا. وعليك إجراء ذلك قبل بدء استخدام الكاميرا كي يتمكّن إطار العمل من ضبط المسارات الداخلية للجهاز وتخصيص مخازن ذاكرة مؤقتة لإرسال الإطارات إلى أهداف الإخراج المطلوبة.

يوضّح مقتطف الرمز التالي كيفية إعداد جلسة كاميرا باستخدام مخزنَين مؤقتَين للمخرجات، أحدهما ينتمي إلى SurfaceView والآخر ImageReader. إنّ إضافة حالة استخدام البث PREVIEW إلى previewSurface و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)
}

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

في هذه المرحلة، لم يتم تحديد الإعدادات النشطة للكاميرا. عند تهيئة الجلسة، يمكنك إنشاء طلبات الالتقاط وإرسالها للقيام بذلك.

إنّ عملية التحويل المطبَّقة على المدخلات عند كتابتها في المستودع الاحتياطي يتم تحديدها حسب نوع كل هدف، والذي يجب أن يكون Surface. يعرف إطار عمل Android كيفية تحويل صورة أولية في التهيئة النشطة إلى تنسيق مناسب لكل هدف. ويتم التحكّم في التحويل من خلال تنسيق البكسل وحجم علامة Surface المحدّدة.

ويبذل إطار العمل قصارى جهده، ولكن قد لا تعمل بعض مجموعات إعدادات Surface، ما يتسبب في حدوث مشاكل مثل عدم إنشاء الجلسة أو حدوث خطأ في وقت التشغيل عند إرسال الطلب أو تدهور الأداء. يوفر إطار العمل ضمانات لمجموعات محددة من معلَمات الأجهزة وشاشات العرض والطلب. توفّر مستندات createCaptureSession() المزيد من المعلومات.

طلبات الالتقاط الفردية

إنّ الإعدادات المستخدَمة لكل إطار يتم ترميزها بترميز 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)

Java

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)

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

عند وضع إطار إخراج في المخزن المؤقت المحدّد، يتم تشغيل استدعاء الالتقاط. في كثير من الحالات، يتم تشغيل عمليات استدعاء إضافية، مثل ImageReader.OnImageAvailableListener، عند معالجة الإطار الذي يحتوي عليه. وفي هذه المرحلة، يمكنك استرداد بيانات الصورة من المخزن المؤقت المحدد.

تكرار طلبات الالتقاط

من السهل تنفيذ طلبات الكاميرا الواحدة، ولكنّها ليست مفيدة للغاية عند عرض معاينة مباشرة أو فيديو. في هذه الحالة، تحتاج إلى تلقي تدفق مستمر من الإطارات، وليس إطارًا واحدًا فقط. يعرض مقتطف الرمز التالي كيفية إضافة طلب متكرّر إلى الجلسة:

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

يؤدي طلب الالتقاط المتكرر إلى التقاط جهاز الكاميرا للصور باستمرار باستخدام الإعدادات في CaptureRequest المقدمة. تتيح واجهة برمجة تطبيقات Camera2 أيضًا للمستخدمين التقاط فيديو من الكاميرا من خلال إرسال تكرار CaptureRequests كما هو موضّح في مستودع عيّنة من Camera2 على GitHub. ويمكنه أيضًا عرض فيديو بالتصوير البطيء من خلال التقاط فيديو عالي السرعة (تصوير بطيء) باستخدام صور متسلسلة متكررة CaptureRequests كما هو موضّح في نموذج تطبيق فيديو التصوير البطيء على GitHub.

طلبات الالتقاط المتداخل

لإرسال طلب التقاط ثانٍ أثناء نشاط طلب الالتقاط المتكرر، مثل عرض عدسة الكاميرا والسماح للمستخدمين بالتقاط صورة، لا تحتاج إلى إيقاف الطلب المتكرر المستمر. وبدلاً من ذلك، يمكنك إصدار طلب التقاط غير متكرر أثناء استمرار تشغيل الطلب المتكرر.

يجب ضبط أي مخزن مؤقت للمخرجات يتم استخدامه كجزء من جلسة الكاميرا عند إنشاء الجلسة لأول مرة. تكون للطلبات المتكررة أولوية أقل من طلبات الإطار الأحادي أو طلبات الصور المتسلسلة، والتي تتيح عمل النموذج التالي:

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

هناك عيب في هذا النهج، على الرغم من ذلك: أنت لا تعرف بالضبط وقت حدوث الطلب الفردي. في الشكل التالي، إذا كان A هو طلب الالتقاط المتكرر وB هو طلب الالتقاط أحادي الإطار، هذه هي الطريقة التي تعالج بها الجلسة قائمة انتظار الطلب:

الشكل 2. صورة توضيحية لقائمة انتظار الطلبات الخاصة بجلسة الكاميرا الجارية

ما من ضمانات بشأن وقت الاستجابة بين آخر طلب متكرّر من A قبل تفعيل الطلب B وفي المرة التالية التي يتم فيها استخدامA مرة أخرى، لذلك قد يتم تخطّي بعض اللقطات. هناك بعض الأشياء التي يمكنك القيام بها للتخفيف من هذه المشكلة:

  • أضِف أهداف المخرجات من الطلب أ إلى الطلب ب. بهذه الطريقة، عندما يكون الإطار B جاهزًا، يتم نسخه إلى أهداف مخرجات A. على سبيل المثال، يعد ذلك ضروريًا عند التقاط لقطات فيديو للحفاظ على ثبات عدد اللقطات في الثانية. في الرمز السابق، يمكنك إضافة singleRequest.addTarget(previewSurface) قبل إنشاء الطلب.

  • استخدِم مجموعة من القوالب المصممة للعمل مع هذا السيناريو تحديدًا، مثل التأخر في حدوث إغلاق تام.