ملاحظة: تشير هذه الصفحة إلى حزمة Camera2. ننصحك باستخدام CameraX، ما لم يتطلّب تطبيقك ميزات محدّدة منخفضة المستوى من Camera2. تتوافق كل من CameraX وCamera2 مع الإصدار 5.0 من نظام التشغيل Android (المستوى 21 من واجهة برمجة التطبيقات) والإصدارات الأحدث.
يمكن أن يحتوي جهاز Android واحد على كاميرات متعددة. كل كاميرا هي
CameraDevice،
ويمكن أن ينتج عن CameraDevice أكثر من بث واحد في الوقت نفسه.
من بين الأسباب التي تدفعك إلى ذلك هو تحسين بث واحد، أي إطارات الكاميرا المتسلسلة الواردة من CameraDevice، لمهمة معيّنة، مثل عرض عدسة الكاميرا، بينما يمكن استخدام إطارات أخرى لالتقاط صورة أو تسجيل فيديو.تعمل عمليات البث كمسارات متوازية تعالج الإطارات الأولية الواردة من الكاميرا، إطارًا تلو الآخر:
تشير المعالجة المتوازية إلى أنّه قد تكون هناك حدود للأداء بناءً على قوة المعالجة المتاحة من وحدة المعالجة المركزية (CPU) أو وحدة معالجة الرسومات (GPU) أو معالج آخر. إذا لم يتمكّن مسار المعالجة من مواكبة اللقطات الواردة، سيبدأ في تجاهلها.
لكل مسار تنسيق إخراج خاص به. يتم تلقائيًا تحويل البيانات الأولية الواردة إلى تنسيق الإخراج المناسب من خلال منطق ضمني مرتبط بكل مسار. إنّ السمة 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 لأجهزة الجهاز تحسين هذه عمليات البث بشكل أكبر.
// 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) }
في هذه المرحلة، لم تحدّد الإعدادات النشطة للكاميرا. عند ضبط الجلسة، يمكنك إنشاء طلبات التقاط وإرسالها لإجراء ذلك.
يتم تحديد التحويل المطبَّق على المدخلات أثناء كتابتها في المخزن المؤقت من خلال نوع كل هدف، والذي يجب أن يكون Surface. يعرف إطار عمل Android كيفية تحويل صورة أولية في الإعدادات النشطة إلى تنسيق مناسب لكل هدف. يتم التحكّم في عملية التحويل من خلال تنسيق البكسل وحجم Surface المحدّد.
يحاول إطار العمل بذل قصارى جهده، ولكن قد لا تعمل بعض مجموعات إعدادات Surface، ما يتسبب في حدوث مشاكل مثل عدم إنشاء الجلسة أو ظهور خطأ أثناء التشغيل عند إرسال طلب أو انخفاض مستوى الأداء. يوفّر إطار العمل ضمانات لمجموعات معيّنة من مَعلمات الأجهزة ومساحات العرض والطلبات. تتوفّر معلومات إضافية في مستندات
createCaptureSession().
طلبات CaptureRequests الفردية
يتم ترميز الإعدادات المستخدَمة لكل لقطة في CaptureRequest، ويتم إرسالها إلى الكاميرا. لإنشاء طلب التقاط، يمكنك استخدام أحد
النماذج
المحدّدة مسبقًا،
أو يمكنك استخدام TEMPLATE_MANUAL للتحكّم الكامل. عند اختيار نموذج، يجب تقديم مخزن مؤقت واحد أو أكثر للإخراج لاستخدامه مع الطلب. يمكنك فقط استخدام المخازن المؤقتة التي تم تحديدها مسبقًا في جلسة التسجيل التي تنوي استخدامها.
تستخدم طلبات الالتقاط
نمط المنشئ
وتمنح المطوّرين فرصة ضبط العديد من الخيارات
المختلفة بما في ذلك
التعرّض التلقائي للضوء
والتركيز التلقائي
وفتحة العدسة.
قبل ضبط حقل، تأكَّد من أنّ الخيار المحدّد متاح للجهاز من خلال استدعاء CameraCharacteristics.getAvailableCaptureRequestKeys() ومن أنّ القيمة المطلوبة متوافقة من خلال التحقّق من سمة الكاميرا المناسبة، مثل أوضاع التعرّض التلقائي المتاحة.
لإنشاء طلب التقاط لـ SurfaceView باستخدام النموذج
المصمّم للمعاينة بدون أي تعديلات، استخدِم
CameraDevice.TEMPLATE_PREVIEW:
val session: CameraCaptureSession = /* ... */ // from CameraCaptureSession.StateCallback val captureRequest = session.device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW) captureRequest.addTarget(previewSurface)
بعد تحديد طلب التقاط، يمكنك الآن إرساله إلى جلسة الكاميرا:
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback val captureRequest: CaptureRequest.Builder = ... // 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،
عند معالجة الإطار الذي يحتوي عليه. في هذه المرحلة، يمكنك استرداد بيانات الصورة من المخزن المؤقت المحدّد.
تكرار طلبات CaptureRequests
من السهل تنفيذ طلبات الكاميرا الواحدة، ولكنّها ليست مفيدة جدًا لعرض معاينة مباشرة أو فيديو. في هذه الحالة، تحتاج إلى تلقي دفق مستمر من الإطارات، وليس إطارًا واحدًا فقط. يوضّح مقتطف الرمز التالي كيفية إضافة طلب متكرّر إلى الجلسة:
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback val captureRequest: CaptureRequest.Builder = ... // 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 API أيضًا للمستخدمين تسجيل الفيديو من الكاميرا من خلال إرسال طلبات CaptureRequests متكررة كما هو موضّح في مستودع عيّنة Camera2 على GitHub. ويمكنه أيضًا عرض فيديو بالتصوير البطيء من خلال التقاط فيديو عالي السرعة (تصوير بطيء) باستخدام لقطات متكررة CaptureRequests كما هو موضّح في نموذج تطبيق فيديو التصوير البطيء Camera2 على GitHub.
تضمين طلبات CaptureRequests
لإرسال طلب التقاط ثانٍ أثناء نشاط طلب الالتقاط المتكرر، مثل عرض عدسة الكاميرا والسماح للمستخدمين بالتقاط صورة، لا تحتاج إلى إيقاف الطلب المتكرر الجاري. بدلاً من ذلك، يمكنك إصدار طلب التقاط غير متكرر أثناء استمرار تشغيل الطلب المتكرر.
يجب ضبط أي مخزن مؤقت للإخراج يتم استخدامه كجزء من جلسة الكاميرا عند إنشاء الجلسة لأول مرة. تكون الأولوية للطلبات المتكررة أقل من طلبات اللقطة الواحدة أو اللقطات المتتالية، ما يتيح عمل النموذج التالي:
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)
ومع ذلك، هناك عيب في هذا النهج: أنت لا تعرف بالضبط متى يحدث الطلب الفردي. في الشكل التالي، إذا كان A هو طلب الالتقاط المتكرر وB هو طلب التقاط الإطار الواحد، فإليك كيفية معالجة الجلسة لقائمة انتظار الطلبات:
لا توجد ضمانات لزمن الاستجابة بين آخر طلب متكرر من A قبل تفعيل الطلب B والمرة التالية التي يتم فيها استخدام A مرة أخرى، لذا قد تواجه بعض اللقطات التي تم تخطيها. هناك بعض الإجراءات التي يمكنك اتّخاذها للحدّ من هذه المشكلة:
أضِف أهداف الإخراج من الطلب أ إلى الطلب ب. بهذه الطريقة، عندما يصبح إطار B جاهزًا، يتم نسخه إلى أهداف الإخراج في A. على سبيل المثال، هذا ضروري عند التقاط لقطات فيديو للحفاظ على معدل إطارات ثابت. في الرمز السابق، يمكنك إضافة
singleRequest.addTarget(previewSurface)قبل إنشاء الطلب.استخدِم مجموعة من النماذج المصمّمة للعمل في هذا السيناريو المحدّد، مثل ميزة "الالتقاط الفوري".