إذا كان تطبيقك يستخدم الإصدار الأصلي Camera
الفئة ("الكاميرا1")، التي تم إيقافها منذ
Android 5.0 (المستوى 21 من واجهة برمجة التطبيقات)،
ننصح بشدة بالتحديث إلى واجهة برمجة التطبيقات الحديثة لكاميرا Android. عروض Android
cameraX (كاميرا Jetpack موحّدة وقوية
واجهة برمجة التطبيقات) والكاميرا2 (واجهة برمجة تطبيقات لإطار العمل منخفض المستوى). بالنسبة إلى
للغالبية العظمى من الحالات، ننصحك بنقل تطبيقك إلى CameraX. وفي ما يلي السبب في ذلك:
- سهولة الاستخدام: يتعامل تطبيق CameraX مع التفاصيل المنخفضة المستوى، حتى يمكنك التركيز أقل على إنشاء تجربة كاميرا من البداية والمزيد على التي تميّز تطبيقك.
- معالجة CameraX للتقسيم نيابةً عنك: يحدّ تطبيق CameraX من البيانات على المدى الطويل. تكاليف الصيانة ورمز خاص بالجهاز، ما يوفر تجارب ذات جودة أعلى للمستخدمين. لمزيد من المعلومات، يُرجى الاطّلاع على توافق أفضل مع الجهاز مع CameraX مشاركة المدونة.
- الإمكانات المتقدّمة: تم تصميم تطبيق CameraX بعناية لتنفيذ مهام متقدّمة. وظيفة يسهل دمجها في تطبيقك. على سبيل المثال، يمكنك بسهولة تطبيق تأثير "ضبابي" و"تجميل الوجه" و"نطاق عالي الديناميكية" و"درجة سطوع منخفضة" استخدِم وضع "الالتقاط الليلي" لصورك باستخدام إضافات CameraX.
- إمكانية التحديث: يُطرح Android إمكانات جديدة وإصلاحات للأخطاء في تطبيق CameraX. على مدار العام. من خلال الانتقال إلى استخدام CameraX، سيحصل تطبيقك على أحدث إصدار من نظام التشغيل Android. تكنولوجيا الكاميرا مع كل إصدار من إصدارات CameraX، وليس فقط في السنوية لإصدارات Android.
تعرّف في هذا الدليل على السيناريوهات الشائعة في تطبيقات الكاميرا. على كل تنفيذ كاميرا 1 وتنفيذ CameraX والمقارنة.
عندما يتعلق الأمر بالنقل، تحتاج أحيانًا إلى مرونة إضافية في الدمج
باستخدام قاعدة رموز حالية تتضمن كل رموز CameraX في هذا الدليل
CameraController
طريقة رائعة إذا كنت تريد أبسط طريقة لاستخدام CameraX، وأيضًا
CameraProvider
والتنفيذ، وهذا أمر رائع إذا كنت بحاجة إلى مزيد من المرونة. لمساعدتك في تحديد
الخيار المناسب لك، وإليك فوائد كل منهما:
وحدة تحكُّم في الكاميرا |
موفّر الكاميرا |
يتطلب القليل من رمز الإعداد | إمكانية التحكّم بشكل أكبر |
يعني السماح للكاميرا X بمعالجة المزيد من عملية الإعداد وتعمل وظائف مثل الضغط للتركيز والتكبير أو التصغير، تلقائيًا |
بما أنّ مطوّر التطبيقات هو المسؤول عن عملية الإعداد، هناك المزيد من الفرص
لتخصيص الإعدادات، مثل تفعيل تدوير الصورة الناتجة
أو ضبط تنسيق صورة الناتج في ImageAnalysis
|
من خلال طلب "PreviewView " لمعاينة الكاميرا، يمكنك
CameraX لتوفير تكامل سلس بين الأطراف، كما هو الحال في حزمة تعلّم الآلة
من التكامل الذي يمكنه تعيين إحداثيات نتائج نموذج تعلُّم الآلة (مثل
المربعات المحيطية) مباشرةً على إحداثيات المعاينة
|
تسمح إمكانية استخدام "سطح" مخصص لمعاينة الكاميرا المزيد من المرونة، مثل استخدام رمز "Surface" الحالي الذي يمكن أن يكون مدخلاً إلى أجزاء أخرى من التطبيق |
إذا واجهتك مشكلة أثناء محاولة نقل البيانات، يُرجى التواصل معنا على مجموعة مناقشة CameraX.
قبل النقل
مقارنة استخدام الكاميراX مع الكاميرا 1
وعلى الرغم من أن الرمز قد يبدو مختلفًا، إلا أن المفاهيم الأساسية في تطبيق Camera1
أما تطبيق CameraX، فهو متشابه إلى حد كبير. الكاميراX
تجريد وظائف الكاميرا الشائعة في حالات الاستخدام،
ونتيجةً لذلك، يتم تنفيذ العديد من المهام التي تُركت للمطوّر في تطبيق "الكاميرا1"
تلقائيًا باستخدام CameraX. هناك أربعة
UseCase
في CameraX، الذي يمكنك
استخدامه لتنفيذ العديد من مهام الكاميرا: Preview
ImageCapture
,
VideoCapture
،
ImageAnalysis
.
أحد الأمثلة على التعامل مع التفاصيل المنخفضة المستوى لمطوّري البرامج في CameraX هو
ViewPort
تتم مشاركته بين
UseCase
نشط. يضمن ذلك رؤية جميع UseCase
وحدات بكسل نفسها بالضبط.
أمّا في Camera1، فعليك إدارة هذه التفاصيل بنفسك، مع الأخذ في الاعتبار التباين
بنِسب العرض إلى الارتفاع على جميع الأجهزة وأجهزة الاستشعار في الكاميرا، فقد يكون من الصعب
لضمان تطابق المعاينة مع الصور والفيديوهات التي تم التقاطها.
كمثال آخر، تعالج CameraX Lifecycle
عمليات معاودة الاتصال تلقائيًا على
Lifecycle
مرة نجحت في تجاوزها. يعني هذا أنّ تطبيق CameraX يعالج
الاتصال بالكاميرا خلال
مراحل نشاط Android،
بما في ذلك الحالات التالية: إغلاق الكاميرا عند انتقال التطبيق إلى
الخلفية؛ إزالة معاينة الكاميرا عندما لا تتطلب الشاشة
عرضه وإيقاف معاينة الكاميرا مؤقتًا عندما يتم إجراء نشاط آخر
أولوية المقدّمة، مثل مكالمة فيديو واردة
وأخيرًا، يتعامل CameraX مع التدوير وتغيير الحجم بدون الحاجة إلى أي رمز إضافي.
من جانبك. في حال توجيه Activity
باتجاه غير مقفل، يعني ذلك أنّ
يتم إعداد ميزة "UseCase
" في كل مرة يتم فيها تدوير الجهاز، حيث يتعطّل النظام.
وتعيد إنشاء Activity
عند تغيير الاتجاه. وينتج عن ذلك
يتم ضبط تدوير الجهاز "UseCases
" ليطابق اتجاه الشاشة حسب
افتراضيًا في كل مرة.
مزيد من المعلومات حول عمليات التدوير في CameraX
قبل الانتقال إلى التفاصيل، إليك نظرة متعمّقة على ميزات الكاميرا
UseCase
وكيف يرتبط تطبيق Camera1. (تتوفّر مفاهيم CameraX في
blue والكاميرا1
المفاهيم
أخضر).
الكاميراX |
|||
إعداد CameraController / CameraProvider | |||
↓ | ↓ | ↓ | ↓ |
معاينة | التقاط الصور | التقاط الفيديو | تحليل الصور |
⁞ | ⁞ | ⁞ | ⁞ |
إدارة سطح المعاينة وضبطه على الكاميرا | ضبط PictureCallback واستدعاء TakePicture() على الكاميرا | إدارة ضبط الكاميرا وMediaRecorder بترتيب معيّن | رمز تحليل مخصّص مضمّن في أعلى سطح المعاينة |
↑ | ↑ | ↑ | ↑ |
الرمز الخاص بالجهاز | |||
↑ | |||
إدارة تدوير الجهاز وقياسه | |||
↑ | |||
إدارة جلسة الكاميرا (اختيار الكاميرا وإدارة مراحل النشاط) | |||
الكاميرا1 |
التوافق والأداء في CameraX
يتوافق تطبيق CameraX مع الأجهزة التي تعمل. Android 5.0 (المستوى 21 من واجهة برمجة التطبيقات) والإصدارات الأحدث. هذا النمط يمثل أكثر من 98% من أجهزة Android الحالية. تم تصميم كاميرا CameraX للتعامل مع الاختلافات بين الأجهزة تلقائيًا، مما يقلل الحاجة إلى ميزات الرمز البرمجي في تطبيقك. علاوةً على ذلك، نختبر ما يزيد عن 150 جهازًا ماديًا على جميع أجهزة Android بالإصدار 5.0 في مركز اختبار CameraX. إِنْتَ مراجعة القائمة الكاملة الأجهزة الموجودة حاليًا في مركز الاختبار الافتراضي.
يستخدم CameraX Executor
من أجل
قيادة حزمة الكاميرا. يمكنك
إعداد منفِّذ العمل الخاص بك على CameraX
إذا كان لتطبيقك متطلبات محدَّدة لسلاسل المحادثات وإذا لم يتم ضبط السياسة، سينشئ تطبيق CameraX.
وتستخدم Executor
تلقائيًا محسَّنًا داخليًا. العديد من واجهات برمجة التطبيقات للنظام الأساسي على
التي تم إنشاؤها باستخدام CameraX والتي تتطلب حظر الاتصال البيني للعمليات (IPC) مع
على أجهزة قد تستغرق أحيانًا مئات المللي ثانية للاستجابة. لهذا الغرض
فإن CameraX لا يستدعي واجهات برمجة التطبيقات هذه إلا من سلاسل محادثات الخلفية، مما يضمن
عدم حظر سلسلة التعليمات الرئيسية وأن واجهة المستخدم تظل سلسة.
اطّلِع على مزيد من المعلومات عن سلاسل المحادثات.
وإذا كان السوق المستهدف لتطبيقك يشمل أجهزة منخفضة المواصفات، يوفر تطبيق CameraX
طريقة لتقليل وقت الإعداد باستخدام
محدّد الكاميرا. نظرًا لأن
عملية الاتصال بمكونات الأجهزة يمكن أن تستغرق قدرًا غير تافه من
الوقت، خاصة على الأجهزة المنخفضة المواصفات، يمكنك تحديد مجموعة الكاميرات التي يتضمنها تطبيقك
احتياجاتهم. لا يتصل CameraX بهذه الكاميرات إلا أثناء الإعداد. على سبيل المثال، إذا
يستخدم التطبيق الكاميرات الخلفية فقط، فيمكنه ضبط هذه الإعدادات
مع DEFAULT_BACK_CAMERA
ثم يتجنب CameraX إعداد الواجهة الأمامية
الكاميرات لتقليل وقت الاستجابة.
مفاهيم تطوير Android
يفترض هذا الدليل إلمامًا عامًا بتطوير تطبيقات Android. ما وراء الأساسيات، فإليك بعض المفاهيم التي من المفيد فهمها قبل الانتقال إلى التعليمة البرمجية أدناه:
- يؤدي ربط العرض إلى إنشاء فئة ربط
ملفات تخطيط XML، مما يتيح لك بسهولة
الإشارة إلى مشاهداتك في صفحة "الأنشطة"
كما هي الحال في العديد من مقتطفات الرمز أدناه. تتوفر بعض
الاختلافات بين ربط العرض و
findViewById()
(الطريقة السابقة للرجوع إلى طرق العرض)، ولكن باستخدام الرمز أدناه، يجب أن تتمكن من استبدال خطوط ربط العرض باستدعاءfindViewById()
مشابه. - الكوروتينات غير المتزامنة عبارة عن تصميم متزامن
التي تمت إضافتها في Kotlin 1.3 ويمكن استخدامها للتعامل مع طرق CameraX التي
عرض
ListenableFuture
. أصبحت هذه الميزة أسهل باستخدام Jetpack مكتبة متزامنة اعتبارًا من الإصدار 1.1.0. لإضافة كوروتين غير متزامن إلى تطبيقك:- إضافة الوضع "
implementation("androidx.concurrent:concurrent-futures-ktx:1.1.0")
" إلى ملف Gradle الخاص بك. - ضع أي رمز CameraX يعرض
ListenableFuture
فيlaunch
حظر أو دالة تعليق. - إضافة
await()
إلى استدعاء الدالة الذي يُرجعListenableFuture
. - للحصول على فهم أعمق لكيفية عمل الكوروتينات، راجع دليل بدء إعداد الكوروتين
- إضافة الوضع "
نقل السيناريوهات الشائعة
يوضّح هذا القسم كيفية نقل السيناريوهات الشائعة من Camera1 إلى CameraX.
يشمل كل سيناريو استخدام Camera1، وكاميرا CameraX CameraProvider
.
وتنفيذه واستخدام CameraX CameraController
.
اختيار كاميرا
في تطبيق الكاميرا، من أول الأشياء التي قد ترغب في تقديمها لاختيار كاميرات مختلفة.
الكاميرا1
في تطبيق Camera1، يمكنك إما الاتصال
Camera.open()
بدون معلَمات
لفتح أول كاميرا خلفية، أو يمكنك تمرير رقم تعريف صحيح
الكاميرا التي تريد فتحها. فيما يلي مثال على كيف يمكن أن يبدو ذلك:
// Camera1: select a camera from id. // Note: opening the camera is a non-trivial task, and it shouldn't be // called from the main thread, unlike CameraX calls, which can be // on the main thread since CameraX kicks off background threads // internally as needed. private fun safeCameraOpen(id: Int): Boolean { return try { releaseCameraAndPreview() camera = Camera.open(id) true } catch (e: Exception) { Log.e(TAG, "failed to open camera", e) false } } private fun releaseCameraAndPreview() { preview?.setCamera(null) camera?.release() camera = null }
CameraX: CameraController
في CameraX، يتولّى الصف CameraSelector
اختيار الكاميرا. الكاميراX
يجعل الأمر الشائع لاستخدام الكاميرا الافتراضية أمرًا سهلاً. يمكنك تحديد ما إذا
تريد استخدام الكاميرا الأمامية التلقائية أو الكاميرا الخلفية التلقائية. بالإضافة إلى ذلك،
يتيح لك الكائن CameraControl
في CameraX تنفيذ إجراءات
ضبط مستوى التكبير/التصغير لتطبيقك، بحيث إذا
تطبيقك يعمل على جهاز متوافق
الكاميرات المنطقية، ثم يتم التبديل
إلى العدسة المناسبة.
في ما يلي رمز CameraX لاستخدام الكاميرا الخلفية التلقائية مع
CameraController
:
// CameraX: select a camera with CameraController var cameraController = LifecycleCameraController(baseContext) val selector = CameraSelector.Builder() .requireLensFacing(CameraSelector.LENS_FACING_BACK).build() cameraController.cameraSelector = selector
CameraX: CameraProvider
في ما يلي مثال على اختيار الكاميرا الأمامية التلقائية باستخدام CameraProvider
(يمكن استخدام الكاميرا الأمامية أو الخلفية إما مع CameraController
أو
CameraProvider
):
// CameraX: select a camera with CameraProvider. // Use await() within a suspend function to get CameraProvider instance. // For more details on await(), see the "Android development concepts" // section above. private suspend fun startCamera() { val cameraProvider = ProcessCameraProvider.getInstance(this).await() // Set up UseCases (more on UseCases in later scenarios) var useCases:Array= ... // Set the cameraSelector to use the default front-facing (selfie) // camera. val cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA try { // Unbind UseCases before rebinding. cameraProvider.unbindAll() // Bind UseCases to camera. This function returns a camera // object which can be used to perform operations like zoom, // flash, and focus. var camera = cameraProvider.bindToLifecycle( this, cameraSelector, useCases) } catch(exc: Exception) { Log.e(TAG, "UseCase binding failed", exc) } }) ... // Call startCamera in the setup flow of your app, such as in onViewCreated. override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) ... lifecycleScope.launch { startCamera() } }
إذا كنت تريد التحكم في الكاميرا التي يتم اختيارها، يمكن أيضًا في هذه الحالة
CameraX إذا كنت تستخدم CameraProvider
من خلال الاتصال
getAvailableCameraInfos()
,
والذي يمنحك كائن CameraInfo
للتحقّق من خصائص كاميرا معيّنة مثل
isFocusMeteringSupported()
يمكنك بعد ذلك تحويله إلى CameraSelector
لاستخدامه على النحو الوارد أعلاه.
أمثلة على طريقة CameraInfo.getCameraSelector()
.
يمكنك الحصول على مزيد من التفاصيل حول كل كاميرا باستخدام
Camera2CameraInfo
الصف. اتصل
getCameraCharacteristic()
مع مفتاح لبيانات الكاميرا التي تريدها. اطّلِع على
CameraCharacteristics
الفئة لقائمة بجميع المفاتيح التي يمكنك الاستعلام عنها.
إليك مثال باستخدام دالة checkFocalLength()
مخصصة يمكنك
حدد نفسك:
// CameraX: get a cameraSelector for first camera that matches the criteria // defined in checkFocalLength(). val cameraInfo = cameraProvider.getAvailableCameraInfos() .first { cameraInfo -> val focalLengths = Camera2CameraInfo.from(cameraInfo) .getCameraCharacteristic( CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS ) return checkFocalLength(focalLengths) } val cameraSelector = cameraInfo.getCameraSelector()
عرض معاينة
تحتاج معظم تطبيقات الكاميرا إلى عرض خلاصة الكاميرا على الشاشة في بعض نقطة واحدة. باستخدام الكاميرا1، عليك إدارة عمليات معاودة الاتصال بمراحل النشاط بشكل صحيح كما يجب عليك تحديد التدوير والتحجيم للمعاينة.
علاوة على ذلك، في تطبيق "الكاميرا1"، يجب أن تقرر ما إذا كنت تريد استخدام
TextureView
أو
SurfaceView
كمساحة عرض للمعاينة
يأتي كلا الخيارين بمفاضلات، وفي كلتا الحالتين، تتطلب منك Camera1 ما يلي:
التعامل مع التدوير والتحجيم بشكل صحيح. PreviewView
الخاص بـ CameraX، على الجانب الآخر
لها عمليات تنفيذ أساسية لكل من TextureView
وSurfaceView
.
ويحدّد تطبيق CameraX التطبيق الأفضل بناءً على عوامل مثل
ونوع الجهاز وإصدار Android الذي يعمل عليه تطبيقك. إذا كانت إحدى
الأخرى، يمكنك إعلان تفضيلك باستخدام
PreviewView.ImplementationMode
يستخدم الخيار COMPATIBLE
علامة TextureView
للمعاينة.
تستخدم القيمة PERFORMANCE
السمة SurfaceView
(إن أمكن ذلك).
الكاميرا1
لعرض معاينة، عليك كتابة صفك الخاص في Preview
باستخدام
تنفيذ
android.view.SurfaceHolder.Callback
تُستخدم لتمرير بيانات الصور من جهاز الكاميرا إلى
التطبيق. وقبل أن تتمكّن من بدء معاينة الصورة المباشرة، انقر على "Preview
"
يجب تمرير الفئة إلى العنصر Camera
.
// Camera1: set up a camera preview. class Preview( context: Context, private val camera: Camera ) : SurfaceView(context), SurfaceHolder.Callback { private val holder: SurfaceHolder = holder.apply { addCallback(this@Preview) setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS) } override fun surfaceCreated(holder: SurfaceHolder) { // The Surface has been created, now tell the camera // where to draw the preview. camera.apply { try { setPreviewDisplay(holder) startPreview() } catch (e: IOException) { Log.d(TAG, "error setting camera preview", e) } } } override fun surfaceDestroyed(holder: SurfaceHolder) { // Take care of releasing the Camera preview in your activity. } override fun surfaceChanged(holder: SurfaceHolder, format: Int, w: Int, h: Int) { // If your preview can change or rotate, take care of those // events here. Make sure to stop the preview before resizing // or reformatting it. if (holder.surface == null) { return // The preview surface does not exist. } // Stop preview before making changes. try { camera.stopPreview() } catch (e: Exception) { // Tried to stop a non-existent preview; nothing to do. } // Set preview size and make any resize, rotate or // reformatting changes here. // Start preview with new settings. camera.apply { try { setPreviewDisplay(holder) startPreview() } catch (e: Exception) { Log.d(TAG, "error starting camera preview", e) } } } } class CameraActivity : AppCompatActivity() { private lateinit var viewBinding: ActivityMainBinding private var camera: Camera? = null private var preview: Preview? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewBinding = ActivityMainBinding.inflate(layoutInflater) setContentView(viewBinding.root) // Create an instance of Camera. camera = getCameraInstance() preview = camera?.let { // Create the Preview view. Preview(this, it) } // Set the Preview view as the content of the activity. val cameraPreview: FrameLayout = viewBinding.cameraPreview cameraPreview.addView(preview) } }
CameraX: CameraController
أمّا في CameraX، فليس هناك الكثير مما تحتاجه أنت بصفتك مطوّر البرامج لإدارته. إذا كنت تستخدم
CameraController
، وبالتالي عليك أيضًا استخدام PreviewView
. وهذا يعني أن
تم تضمين Preview
UseCase
، ما يجعل عملية الإعداد أقل بكثير:
// CameraX: set up a camera preview with a CameraController. class MainActivity : AppCompatActivity() { private lateinit var viewBinding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewBinding = ActivityMainBinding.inflate(layoutInflater) setContentView(viewBinding.root) // Create the CameraController and set it on the previewView. var cameraController = LifecycleCameraController(baseContext) cameraController.bindToLifecycle(this) val previewView: PreviewView = viewBinding.cameraPreview previewView.controller = cameraController } }
CameraX: CameraProvider
باستخدام CameraProvider
في CameraX، لن تحتاج إلى استخدام PreviewView
، بل يجب
يعمل على تبسيط إعداد المعاينة بشكل كبير عبر الكاميرا1. للعرض التوضيحي
يستخدم هذا المثال العلامة PreviewView
، ولكن يمكنك كتابة
يمكنك تمرير SurfaceProvider
إلى setSurfaceProvider()
إذا كانت لديك أسئلة أكثر تعقيدًا.
احتياجاتهم.
هنا، لم يتم تضمين UseCase
Preview
كما هو الحال مع CameraController
،
لذا عليك إعداده:
// CameraX: set up a camera preview with a CameraProvider. // Use await() within a suspend function to get CameraProvider instance. // For more details on await(), see the "Android development concepts" // section above. private suspend fun startCamera() { val cameraProvider = ProcessCameraProvider.getInstance(this).await() // Create Preview UseCase. val preview = Preview.Builder() .build() .also { it.setSurfaceProvider( viewBinding.viewFinder.surfaceProvider ) } // Select default back camera. val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA try { // Unbind UseCases before rebinding. cameraProvider.unbindAll() // Bind UseCases to camera. This function returns a camera // object which can be used to perform operations like zoom, // flash, and focus. var camera = cameraProvider.bindToLifecycle( this, cameraSelector, useCases) } catch(exc: Exception) { Log.e(TAG, "UseCase binding failed", exc) } }) ... // Call startCamera() in the setup flow of your app, such as in onViewCreated. override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) ... lifecycleScope.launch { startCamera() } }
النقر للتركيز
عند عرض معاينة الكاميرا على الشاشة، من الشائع ضبط البؤرة. عندما ينقر المستخدم على المعاينة.
الكاميرا1
لاستخدام ميزة النقر للتركيز في الكاميرا 1، يجب حساب التركيز الأمثل.
Area
للإشارة إلى المكان الذي يجب أن يحاول فيه Camera
التركيز. هذا Area
هو
يتم إرسالها إلى setFocusAreas()
. كما يجب عليك ضبط وضع تركيز متوافق على
Camera
لن يكون لمنطقة التركيز تأثير إلا إذا كان وضع التركيز الحالي هو
FOCUS_MODE_AUTO
أو FOCUS_MODE_MACRO
أو FOCUS_MODE_CONTINUOUS_VIDEO
أو
FOCUS_MODE_CONTINUOUS_PICTURE
كل Area
عبارة عن مستطيل بوزن محدد. الوزن قيمة بين
1 و1000، وتُستخدَم لمنح الأولوية للتركيز Areas
في حال ضبط قيم متعددة. هذا النمط
مثال واحد فقط Area
، لذا فإن قيمة الوزن لا يهم. إحداثيات
نطاق المستطيل من -1000 إلى 1000. والنقطة العلوية اليسرى هي (-1000، -1000).
النقطة اليمنى السفلية هي (1000، 1000). يرتبط الاتجاه بأداة الاستشعار
الاتجاه، أي ما يراه المستشعر. ولا يتأثر الاتجاه
تدوير أو نسخ Camera.setDisplayOrientation()
، ولذلك يجب
تحويل إحداثيات حدث اللمس إلى إحداثيات أداة الاستشعار.
// Camera1: implement tap-to-focus. class TapToFocusHandler : Camera.AutoFocusCallback { private fun handleFocus(event: MotionEvent) { val camera = camera ?: return val parameters = try { camera.getParameters() } catch (e: RuntimeException) { return } // Cancel previous auto-focus function, if one was in progress. camera.cancelAutoFocus() // Create focus Area. val rect = calculateFocusAreaCoordinates(event.x, event.y) val weight = 1 // This value's not important since there's only 1 Area. val focusArea = Camera.Area(rect, weight) // Set the focus parameters. parameters.setFocusMode(Parameters.FOCUS_MODE_AUTO) parameters.setFocusAreas(listOf(focusArea)) // Set the parameters back on the camera and initiate auto-focus. camera.setParameters(parameters) camera.autoFocus(this) } private fun calculateFocusAreaCoordinates(x: Int, y: Int) { // Define the size of the Area to be returned. This value // should be optimized for your app. val focusAreaSize = 100 // You must define functions to rotate and scale the x and y values to // be values between 0 and 1, where (0, 0) is the upper left-hand side // of the preview, and (1, 1) is the lower right-hand side. val normalizedX = (rotateAndScaleX(x) - 0.5) * 2000 val normalizedY = (rotateAndScaleY(y) - 0.5) * 2000 // Calculate the values for left, top, right, and bottom of the Rect to // be returned. If the Rect would extend beyond the allowed values of // (-1000, -1000, 1000, 1000), then crop the values to fit inside of // that boundary. val left = max(normalizedX - (focusAreaSize / 2), -1000) val top = max(normalizedY - (focusAreaSize / 2), -1000) val right = min(left + focusAreaSize, 1000) val bottom = min(top + focusAreaSize, 1000) return Rect(left, top, left + focusAreaSize, top + focusAreaSize) } override fun onAutoFocus(focused: Boolean, camera: Camera) { if (!focused) { Log.d(TAG, "tap-to-focus failed") } } }
CameraX: CameraController
ينتظر "CameraController
" سماع أحداث اللمس لـ "PreviewView
" للتعامل معها.
انقر للتركيز تلقائيًا. يمكنك تفعيل ميزة "الضغط للتركيز" أو إيقافها باستخدام
setTapToFocusEnabled()
،
ونتحقق من القيمة باستخدام المعلمة المقابلة لها
isTapToFocusEnabled()
تشير رسالة الأشكال البيانية
getTapToFocusState()
تُنتج الدالة LiveData
لتتبّع التغييرات في حالة التركيز على CameraController
.
// CameraX: track the state of tap-to-focus over the Lifecycle of a PreviewView, // with handlers you can define for focused, not focused, and failed states. val tapToFocusStateObserver = Observer{ state -> when (state) { CameraController.TAP_TO_FOCUS_NOT_STARTED -> Log.d(TAG, "tap-to-focus init") CameraController.TAP_TO_FOCUS_STARTED -> Log.d(TAG, "tap-to-focus started") CameraController.TAP_TO_FOCUS_FOCUSED -> Log.d(TAG, "tap-to-focus finished (focus successful)") CameraController.TAP_TO_FOCUS_NOT_FOCUSED -> Log.d(TAG, "tap-to-focus finished (focused unsuccessful)") CameraController.TAP_TO_FOCUS_FAILED -> Log.d(TAG, "tap-to-focus failed") } } cameraController.getTapToFocusState().observe(this, tapToFocusStateObserver)
CameraX: CameraProvider
عند استخدام CameraProvider
، يجب إجراء عملية إعداد للحصول على ميزة "النقر للتركيز".
العمل. يفترض هذا المثال أنك تستخدم السمة PreviewView
. إذا لم يكن الأمر كذلك، فأنت بحاجة إلى
عليك تعديل المنطق لتطبيق Surface
المخصّص.
في ما يلي الخطوات التي يجب اتّباعها عند استخدام "PreviewView
":
- عليك إعداد أداة رصد الإيماءات للتعامل مع أحداث النقر.
- من خلال حدث النقر، يمكنك إنشاء
MeteringPoint
باستخدامMeteringPointFactory.createPoint()
- باستخدام
MeteringPoint
، يمكنك إنشاءFocusMeteringAction
. - مع العنصر
CameraControl
على جهازCamera
(مرتجع منbindToLifecycle()
)، الاتصال برقمstartFocusAndMetering()
، مرورًا فيFocusMeteringAction
- (اختياري) يمكنك الردّ على
FocusMeteringResult
. - يمكنك ضبط أداة رصد الإيماءات للاستجابة إلى أحداث اللمس التي
PreviewView.setOnTouchListener()
// CameraX: implement tap-to-focus with CameraProvider. // Define a gesture detector to respond to tap events and call // startFocusAndMetering on CameraControl. If you want to use a // coroutine with await() to check the result of focusing, see the // "Android development concepts" section above. val gestureDetector = GestureDetectorCompat(context, object : SimpleOnGestureListener() { override fun onSingleTapUp(e: MotionEvent): Boolean { val previewView = previewView ?: return val camera = camera ?: return val meteringPointFactory = previewView.meteringPointFactory val focusPoint = meteringPointFactory.createPoint(e.x, e.y) val meteringAction = FocusMeteringAction .Builder(meteringPoint).build() lifecycleScope.launch { val focusResult = camera.cameraControl .startFocusAndMetering(meteringAction).await() if (!result.isFocusSuccessful()) { Log.d(TAG, "tap-to-focus failed") } } } } ) ... // Set the gestureDetector in a touch listener on the PreviewView. previewView.setOnTouchListener { _, event -> // See pinch-to-zooom scenario for scaleGestureDetector definition. var didConsume = scaleGestureDetector.onTouchEvent(event) if (!scaleGestureDetector.isInProgress) { didConsume = gestureDetector.onTouchEvent(event) } didConsume }
التكبير أو التصغير بإصبعَين
كما أن تكبير المعاينة وتصغيرها هو تلاعب مباشر شائع آخر معاينة الكاميرا. مع تزايد عدد الكاميرات على الأجهزة، يمكن للمستخدمين أيضًا نتوقع أن يتم تحديد العدسة ذات البعد البؤري الأفضل تلقائيًا على أنها نتيجة التكبير أو التصغير.
الكاميرا1
هناك طريقتان للتكبير/التصغير باستخدام الكاميرا1. الطريقة Camera.startSmoothZoom()
متحركة من مستوى التكبير الحالي إلى مستوى التكبير/التصغير الذي تمرره. تشير رسالة الأشكال البيانية
تنتقل طريقة Camera.Parameters.setZoom()
مباشرةً إلى مستوى التكبير/التصغير الذي تجتازه
بوصة قبل استخدام أي منهما، اتصِل بالرقم isSmoothZoomSupported()
أو
isZoomSupported()
على التوالي لضمان اتّباع طرق التكبير/التصغير ذات الصلة التي تحتاج إليها
المتوفرة على الكاميرا.
لتنفيذ التكبير أو التصغير، يستخدم هذا المثال setZoom()
لأنّ اللمسة
يشغّل المستمع على سطح المعاينة الأحداث باستمرار أثناء التصغير أو التكبير بإصبعين
حدوث إيماءة، لذا يتم تحديث مستوى التكبير/التصغير فورًا في كل مرة. تشير رسالة الأشكال البيانية
الفئة ZoomTouchListener
محدّدة أدناه، ويجب ضبطها كاستدعاء اتصال.
إلى أداة معالجة اللمس على مساحة المعاينة
// Camera1: implement pinch-to-zoom. // Define a scale gesture detector to respond to pinch events and call // setZoom on Camera.Parameters. val scaleGestureDetector = ScaleGestureDetector(context, object : ScaleGestureDetector.OnScaleGestureListener { override fun onScale(detector: ScaleGestureDetector): Boolean { val camera = camera ?: return false val parameters = try { camera.parameters } catch (e: RuntimeException) { return false } // In case there is any focus happening, stop it. camera.cancelAutoFocus() // Set the zoom level on the Camera.Parameters, and set // the Parameters back onto the Camera. val currentZoom = parameters.zoom parameters.setZoom(detector.scaleFactor * currentZoom) camera.setParameters(parameters) return true } } ) // Define a View.OnTouchListener to attach to your preview view. class ZoomTouchListener : View.OnTouchListener { override fun onTouch(v: View, event: MotionEvent): Boolean = scaleGestureDetector.onTouchEvent(event) } // Set a ZoomTouchListener to handle touch events on your preview view // if zoom is supported by the current camera. if (camera.getParameters().isZoomSupported()) { view.setOnTouchListener(ZoomTouchListener()) }
CameraX: CameraController
تمامًا مثل النقر للتركيز، يستمع CameraController
إلى لمس المعاينة
الأحداث للتعامل مع التصغير أو التكبير تلقائيًا. يمكنك تفعيل الميزة وإيقافها
التصغير أو التكبير بإصبعين باستخدام
setPinchToZoomEnabled()
،
ونتحقق من القيمة باستخدام المعلمة المقابلة لها
isPinchToZoomEnabled()
تشير رسالة الأشكال البيانية
getZoomState()
يتم إرجاع الكائن LiveData
لتتبع التغييرات على
ZoomState
على
CameraController
// CameraX: track the state of pinch-to-zoom over the Lifecycle of // a PreviewView, logging the linear zoom ratio. val pinchToZoomStateObserver = Observer{ state -> val zoomRatio = state.getZoomRatio() Log.d(TAG, "ptz-zoom-ratio $zoomRatio") } cameraController.getZoomState().observe(this, pinchToZoomStateObserver)
CameraX: CameraProvider
لتفعيل ميزة التكبير/التصغير بإصبعَين مع CameraProvider
، يجب ضبط بعض الإعدادات. في حال حذف
لا تستخدم PreviewView
، فينبغي عليك تعديل المنطق لتطبيقه على
مخصص Surface
.
في ما يلي الخطوات التي يجب اتّباعها عند استخدام "PreviewView
":
- عليك إعداد أداة رصد إيماءات على شكل مقياس للتعامُل مع أحداث التصغير والتكبير.
- يمكنك الحصول على
ZoomState
من الكائنCamera.CameraInfo
، حيث يتم استبعادCamera
. يتم إرجاع المثيل عند استدعاءbindToLifecycle()
- إذا كانت قيمة
ZoomState
تساويzoomRatio
، احفظها كعنصر التكبير الحالي. النسبة المئوية. في حال عدم توفّرzoomRatio
علىZoomState
، استخدِم الإعدادات التلقائية للكاميرا معدل التكبير/التصغير (1.0). - استخدِم ناتج نسبة التكبير/التصغير الحالية باستخدام
scaleFactor
إلى لتحديد نسبة التكبير/التصغير الجديدة، وتمريره إلىCameraControl.setZoomRatio()
. - يمكنك ضبط أداة رصد الإيماءات للاستجابة إلى أحداث اللمس التي
PreviewView.setOnTouchListener()
// CameraX: implement pinch-to-zoom with CameraProvider. // Define a scale gesture detector to respond to pinch events and call // setZoomRatio on CameraControl. val scaleGestureDetector = ScaleGestureDetector(context, object : SimpleOnGestureListener() { override fun onScale(detector: ScaleGestureDetector): Boolean { val camera = camera ?: return val zoomState = camera.cameraInfo.zoomState val currentZoomRatio: Float = zoomState.value?.zoomRatio ?: 1f camera.cameraControl.setZoomRatio( detector.scaleFactor * currentZoomRatio ) } } ) ... // Set the scaleGestureDetector in a touch listener on the PreviewView. previewView.setOnTouchListener { _, event -> var didConsume = scaleGestureDetector.onTouchEvent(event) if (!scaleGestureDetector.isInProgress) { // See pinch-to-zooom scenario for gestureDetector definition. didConsume = gestureDetector.onTouchEvent(event) } didConsume }
التقاط صورة
يوضح هذا القسم كيفية تشغيل التقاط الصور، وما إذا كنت تريد القيام بذلك على الضغط على زر الغالق أو بعد انتهاء الموقّت أو في أي حدث آخر الاختيار.
الكاميرا1
في Camera1، يمكنك أولاً تحديد
Camera.PictureCallback
لإدارة بيانات الصورة عند طلبها. فيما يلي مثال بسيط على
PictureCallback
للتعامل مع بيانات صور JPEG:
// Camera1: define a Camera.PictureCallback to handle JPEG data. private val picture = Camera.PictureCallback { data, _ -> val pictureFile: File = getOutputMediaFile(MEDIA_TYPE_IMAGE) ?: run { Log.d(TAG, "error creating media file, check storage permissions") return@PictureCallback } try { val fos = FileOutputStream(pictureFile) fos.write(data) fos.close() } catch (e: FileNotFoundException) { Log.d(TAG, "file not found", e) } catch (e: IOException) { Log.d(TAG, "error accessing file", e) } }
وعند التقاط صورة، عليك استدعاء طريقة takePicture()
على الجهاز الافتراضي Camera
. تعتمد طريقة takePicture()
هذه على ثلاثة أنواع
المعاملات لأنواع البيانات المختلفة. المعلمة الأولى هي
ShutterCallback
(لم يتم تعريفها في هذا المثال). المعلمة الثانية هي
لـ PictureCallback
لمعالجة بيانات الكاميرا الأولية (غير المضغوطة). الثالث
هي التي يستخدمها هذا المثال، حيث إنّها PictureCallback
يجب التعامل معها
بيانات صورة JPEG
// Camera1: call takePicture on Camera instance, passing our PictureCallback. camera?.takePicture(null, null, picture)
CameraX: CameraController
يحافظ CameraController
الخاص بـ CameraX على بساطة الكاميرا1 للصور.
تسجيلها من خلال تنفيذ طريقة takePicture()
الخاصة بها. هنا، حدد
لضبط إدخال MediaStore
والتقاط صورة لحفظها هناك.
// CameraX: define a function that uses CameraController to take a photo. private val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS" private fun takePhoto() { // Create time stamped name and MediaStore entry. val name = SimpleDateFormat(FILENAME_FORMAT, Locale.US) .format(System.currentTimeMillis()) val contentValues = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, name) put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg") if(Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { put(MediaStore.Images.Media.RELATIVE_PATH, "Pictures/CameraX-Image") } } // Create output options object which contains file + metadata. val outputOptions = ImageCapture.OutputFileOptions .Builder(context.getContentResolver(), MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues) .build() // Set up image capture listener, which is triggered after photo has // been taken. cameraController.takePicture( outputOptions, ContextCompat.getMainExecutor(this), object : ImageCapture.OnImageSavedCallback { override fun onError(e: ImageCaptureException) { Log.e(TAG, "photo capture failed", e) } override fun onImageSaved( output: ImageCapture.OutputFileResults ) { val msg = "Photo capture succeeded: ${output.savedUri}" Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show() Log.d(TAG, msg) } } ) }
CameraX: CameraProvider
تتم عملية التقاط صورة باستخدام CameraProvider
بالطريقة نفسها المتّبعة مع التقاط الصور.
CameraController
، ولكن عليك أولاً إنشاء ImageCapture
وربطه.
UseCase
للحصول على كائن لاستدعاء takePicture()
عليه:
// CameraX: create and bind an ImageCapture UseCase. // Make a reference to the ImageCapture UseCase at a scope that can be accessed // throughout the camera logic in your app. private var imageCapture: ImageCapture? = null ... // Create an ImageCapture instance (can be added with other // UseCase definitions). imageCapture = ImageCapture.Builder().build() ... // Bind UseCases to camera (adding imageCapture along with preview here, but // preview is not required to use imageCapture). This function returns a camera // object which can be used to perform operations like zoom, flash, and focus. var camera = cameraProvider.bindToLifecycle( this, cameraSelector, preview, imageCapture)
وبعد ذلك، عندما تريد التقاط صورة، يمكنك الاتصال
ImageCapture.takePicture()
اطّلِع على الرمز CameraController
في هذا القسم.
للحصول على مثال كامل للدالة takePhoto()
.
// CameraX: define a function that uses CameraController to take a photo. private fun takePhoto() { // Get a stable reference of the modifiable ImageCapture UseCase. val imageCapture = imageCapture ?: return ... // Call takePicture on imageCapture instance. imageCapture.takePicture( ... ) }
تسجيل مقطع فيديو
إنّ تسجيل فيديو هو أكثر تعقيدًا بكثير مما كانت عليه السيناريوهات حتى الآن. يجب إعداد كل جزء من العملية بشكل صحيح، عادةً في بترتيب معين. قد يلزمك أيضًا التحقق من تشغيل الفيديو والصوت المزامنة أو التعامل مع التناقضات الإضافية في الجهاز.
وكما ستلاحظ، سيعالج CameraX مرة أخرى الكثير من التعقيد بالنسبة لك.
الكاميرا1
يتطلّب التقاط الفيديو باستخدام الكاميرا 1 إدارة دقيقة لجهاز Camera
MediaRecorder
، كما يجب أن تكون الطرق
بترتيب معين. يجب اتباع هذا الترتيب
تطبيقك ليعمل بشكل صحيح:
- افتح الكاميرا.
- إعداد معاينة وبدء تشغيلها (إذا كان التطبيق يعرض الفيديو الذي يتم تسجيله، وهذا هو الحال عادةً).
- عليك فتح قفل الكاميرا لاستخدامها في تطبيق "
MediaRecorder
" من خلال الاتصال بالرقمCamera.unlock()
. - يمكنك ضبط التسجيل من خلال استدعاء الطرق التالية على
MediaRecorder
:- يمكنك ربط المثيل
Camera
بـsetCamera(camera)
. - الاتصال بالرقم
setAudioSource(MediaRecorder.AudioSource.CAMCORDER)
. - الاتصال بالرقم
setVideoSource(MediaRecorder.VideoSource.CAMERA)
. - الاتصال بـ "
setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_1080P))
" لضبط الجودة. عرضCamcorderProfile
لكل خيارات الجودة. - الاتصال بالرقم
setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString())
. - إذا كان تطبيقك يحتوي على معاينة للفيديو، يمكنك إجراء مكالمة
setPreviewDisplay(preview?.holder?.surface)
- الاتصال بالرقم
setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
. - الاتصال بالرقم
setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT)
. - الاتصال بالرقم
setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT)
. - يمكنك الاتصال بالرقم
prepare()
لإكمال عملية ضبط جهازMediaRecorder
.
- يمكنك ربط المثيل
- لبدء التسجيل، اتصل برقم
MediaRecorder.start()
. - لإيقاف التسجيل، يمكنك طلب الطرق التالية. مرة أخرى، اتبع هذا الترتيب تحديدًا:
- الاتصال بالرقم
MediaRecorder.stop()
. - يمكنك، إن أردت، إزالة إعداد
MediaRecorder
الحالي عن طريق طلبMediaRecorder.reset()
- الاتصال بالرقم
MediaRecorder.release()
. - قفل الكاميرا بحيث يمكن لجلسات
MediaRecorder
المستقبلية استخدامها من خلال يَتِمُّ الْآنَ الِاتِّصَالْ بِـCamera.lock()
.
- الاتصال بالرقم
- لإيقاف المعاينة، يُرجى الاتصال بالرقم
Camera.stopPreview()
. - وأخيرًا، لتحرير
Camera
حتى تتمكن العمليات الأخرى من استخدامها، اطلبCamera.release()
في ما يلي كل هذه الخطوات مجتمعةً:
// Camera1: set up a MediaRecorder and a function to start and stop video // recording. // Make a reference to the MediaRecorder at a scope that can be accessed // throughout the camera logic in your app. private var mediaRecorder: MediaRecorder? = null private var isRecording = false ... private fun prepareMediaRecorder(): Boolean { mediaRecorder = MediaRecorder() // Unlock and set camera to MediaRecorder. camera?.unlock() mediaRecorder?.run { setCamera(camera) // Set the audio and video sources. setAudioSource(MediaRecorder.AudioSource.CAMCORDER) setVideoSource(MediaRecorder.VideoSource.CAMERA) // Set a CamcorderProfile (requires API Level 8 or higher). setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)) // Set the output file. setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO).toString()) // Set the preview output. setPreviewDisplay(preview?.holder?.surface) setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT) setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT) // Prepare configured MediaRecorder. return try { prepare() true } catch (e: IllegalStateException) { Log.d(TAG, "preparing MediaRecorder failed", e) releaseMediaRecorder() false } catch (e: IOException) { Log.d(TAG, "setting MediaRecorder file failed", e) releaseMediaRecorder() false } } return false } private fun releaseMediaRecorder() { mediaRecorder?.reset() mediaRecorder?.release() mediaRecorder = null camera?.lock() } private fun startStopVideo() { if (isRecording) { // Stop recording and release camera. mediaRecorder?.stop() releaseMediaRecorder() camera?.lock() isRecording = false // This is a good place to inform user that video recording has stopped. } else { // Initialize video camera. if (prepareVideoRecorder()) { // Camera is available and unlocked, MediaRecorder is prepared, now // you can start recording. mediaRecorder?.start() isRecording = true // This is a good place to inform the user that recording has // started. } else { // Prepare didn't work, release the camera. releaseMediaRecorder() // Inform user here. } } }
CameraX: CameraController
باستخدام CameraController
في CameraX، يمكنك إيقاف/تفعيل ImageCapture
VideoCapture
، وImageAnalysis
UseCase
كل على حدة،
طالما يمكن استخدام قائمة UseCases بشكل متزامن.
يتم تلقائيًا تفعيل UseCase
وImageCapture
وImageAnalysis
،
هو سبب عدم حاجتك إلى الاتصال بـ setEnabledUseCases()
لالتقاط صورة.
لاستخدام CameraController
لتسجيل الفيديو، يجب أولاً استخدام
setEnabledUseCases()
للسماح VideoCapture
UseCase
.
// CameraX: Enable VideoCapture UseCase on CameraController. cameraController.setEnabledUseCases(VIDEO_CAPTURE);
عندما تريد بدء تسجيل الفيديو، يمكنك الاتصال
CameraController.startRecording()
الأخرى. يمكن لهذه الدالة حفظ الفيديو المسجَّل في File
، كما ترى.
في المثال أدناه. بالإضافة إلى ذلك، عليك اجتياز Executor
وصف.
تنفّذ
OnVideoSavedCallback
للتعامل مع استدعاءات النجاح والخطأ. عند انتهاء التسجيل، اتصل
CameraController.stopRecording()
ملاحظة: إذا كنت تستخدم CameraX 1.3.0-alpha02 أو إصدارًا أحدث، هناك تطبيق إضافي
مَعلمة AudioConfig
تتيح لك تمكين أو تعطيل التسجيل الصوتي في الفيديو. للتفعيل
تسجيل صوتي، فستحتاج إلى التأكد من امتلاكك أذونات استخدام الميكروفون.
بالإضافة إلى ذلك، تتم إزالة الطريقة stopRecording()
في 1.3.0-alpha02،
تعرض الدالة startRecording()
كائن Recording
يمكن استخدامه للإيقاف المؤقت،
واستئناف تسجيل الفيديو وإيقافه.
// CameraX: implement video capture with CameraController. private val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS" // Define a VideoSaveCallback class for handling success and error states. class VideoSaveCallback : OnVideoSavedCallback { override fun onVideoSaved(outputFileResults: OutputFileResults) { val msg = "Video capture succeeded: ${outputFileResults.savedUri}" Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show() Log.d(TAG, msg) } override fun onError(videoCaptureError: Int, message: String, cause: Throwable?) { Log.d(TAG, "error saving video: $message", cause) } } private fun startStopVideo() { if (cameraController.isRecording()) { // Stop the current recording session. cameraController.stopRecording() return } // Define the File options for saving the video. val name = SimpleDateFormat(FILENAME_FORMAT, Locale.US) .format(System.currentTimeMillis()) val outputFileOptions = OutputFileOptions .Builder(File(this.filesDir, name)) .build() // Call startRecording on the CameraController. cameraController.startRecording( outputFileOptions, ContextCompat.getMainExecutor(this), VideoSaveCallback() ) }
CameraX: CameraProvider
إذا كنت تستخدم CameraProvider
، عليك إنشاء VideoCapture
.
UseCase
وتمريرها في كائن Recorder
. في Recorder.Builder
، يمكنك
ضبط جودة الفيديو، واختياريًا
FallbackStrategy
، التي
يعالج الحالات التي لا يستوفي فيها الجهاز مواصفات الجودة المطلوبة. بَعْدَ ذَلِكْ
اربط المثيل VideoCapture
بالسمة CameraProvider
باستخدام
UseCase
// CameraX: create and bind a VideoCapture UseCase with CameraProvider. // Make a reference to the VideoCapture UseCase and Recording at a // scope that can be accessed throughout the camera logic in your app. private lateinit var videoCapture: VideoCaptureprivate var recording: Recording? = null ... // Create a Recorder instance to set on a VideoCapture instance (can be // added with other UseCase definitions). val recorder = Recorder.Builder() .setQualitySelector(QualitySelector.from(Quality.FHD)) .build() videoCapture = VideoCapture.withOutput(recorder) ... // Bind UseCases to camera (adding videoCapture along with preview here, but // preview is not required to use videoCapture). This function returns a camera // object which can be used to perform operations like zoom, flash, and focus. var camera = cameraProvider.bindToLifecycle( this, cameraSelector, preview, videoCapture)
في هذه المرحلة، يمكن الوصول إلى Recorder
على videoCapture.output
.
الموقع. يمكن لـ Recorder
بدء تسجيلات الفيديو المحفوظة في File
،
ParcelFileDescriptor
أو MediaStore
. يستخدم هذا المثال السمة MediaStore
.
في Recorder
، هناك عدة طرق لطلب البيانات من أجل إعدادها. اتصل
prepareRecording()
لضبط خيارات الناتج MediaStore
إذا كان تطبيقك يحتوي على
للحصول على إذن لاستخدام ميكروفون الجهاز، اتصل بـ withAudioEnabled()
أيضًا.
بعد ذلك، يمكنك طلب start()
لبدء التسجيل، ومراعٍ في سياقٍ.
أداة معالجة الأحداث في Consumer<VideoRecordEvent>
للتعامل مع أحداث تسجيل الفيديو. في حال حذف
بنجاح، يمكن استخدام Recording
المعروضة لإيقاف
التسجيل.
// CameraX: implement video capture with CameraProvider. private val FILENAME_FORMAT = "yyyy-MM-dd-HH-mm-ss-SSS" private fun startStopVideo() { val videoCapture = this.videoCapture ?: return if (recording != null) { // Stop the current recording session. recording.stop() recording = null return } // Create and start a new recording session. val name = SimpleDateFormat(FILENAME_FORMAT, Locale.US) .format(System.currentTimeMillis()) val contentValues = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, name) put(MediaStore.MediaColumns.MIME_TYPE, "video/mp4") if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { put(MediaStore.Video.Media.RELATIVE_PATH, "Movies/CameraX-Video") } } val mediaStoreOutputOptions = MediaStoreOutputOptions .Builder(contentResolver, MediaStore.Video.Media.EXTERNAL_CONTENT_URI) .setContentValues(contentValues) .build() recording = videoCapture.output .prepareRecording(this, mediaStoreOutputOptions) .withAudioEnabled() .start(ContextCompat.getMainExecutor(this)) { recordEvent -> when(recordEvent) { is VideoRecordEvent.Start -> { viewBinding.videoCaptureButton.apply { text = getString(R.string.stop_capture) isEnabled = true } } is VideoRecordEvent.Finalize -> { if (!recordEvent.hasError()) { val msg = "Video capture succeeded: " + "${recordEvent.outputResults.outputUri}" Toast.makeText( baseContext, msg, Toast.LENGTH_SHORT ).show() Log.d(TAG, msg) } else { recording?.close() recording = null Log.e(TAG, "video capture ends with error", recordEvent.error) } viewBinding.videoCaptureButton.apply { text = getString(R.string.start_capture) isEnabled = true } } } } }
مصادر إضافية
لدينا العديد من تطبيقات CameraX الكاملة في مستودع GitHub لعيّنات الكاميرا توضح هذه النماذج مدى توافق السيناريوهات في هذا الدليل مع تطبيق Android.
إذا أردت الحصول على دعم إضافي في نقل البيانات إلى CameraX أو كانت لديك أسئلة بخصوص مجموعة واجهات برمجة التطبيقات للكاميرا Android، يُرجى التواصل معنا على مناقشة CameraX المجموعة: