بنية التقاط الفيديوهات باستخدام CameraX

يسجّل نظام الالتقاط بشكل عام عمليات بث الفيديو والصوت ويضغطها مزج البثين، ثم كتابة الدفق الناتج إلى القرص.

مخطط المفاهيمي لنظام التقاط الفيديو والصوت
الشكل 1. رسم تخطيطي مفاهيمي لفيديو وصوت التقاط المحتوى.

في CameraX، يكون حل التقاط الفيديو هو VideoCapture حالة الاستخدام:

رسم بياني مفاهيمي يوضّح طريقة تعامل الكاميرا x مع
         حالة استخدام تصوير فيديو
الشكل 2. رسم بياني مفاهيمي يوضّح طريقة استخدام CameraX حالة استخدام VideoCapture.

كما هو موضح في الشكل 2، يتضمن التقاط فيديو CameraX بعض الطبقات المكونات المعمارية:

  • SurfaceProvider لمصدر الفيديو.
  • AudioSource لمصدر الصوت.
  • برنامجا ترميز لترميز الفيديو/الصوت وضغطه
  • جهاز وسائط لمزج البث المباشر
  • توفير ملفات لكتابة النتيجة

تعمل واجهة برمجة التطبيقات VideoCapture على تجريد محرك الالتقاط المعقد وتوفر ذات واجهة برمجة تطبيقات أبسط ومباشرة.

نظرة عامة حول VideoCapture API

إنّ VideoCapture هي حالة استخدام CameraX تعمل بشكل منفرد أو عندما إلى جانب حالات استخدام أخرى. تعتمد المجموعات المحددة المعتمدة على إمكانات جهاز الكاميرا، ولكن Preview وVideoCapture تركيبة حالة استخدام صالحة على جميع الأجهزة.

تتألف واجهة برمجة التطبيقات VideoCapture من الكائنات التالية التي تتصل مع التطبيقات:

  • VideoCapture هو فئة حالة استخدام عالية المستوى. يرتبط VideoCapture بـ LifecycleOwner مع CameraSelector وجهاز CameraX آخر حالات الاستخدام. لمزيد من المعلومات عن هذه المفاهيم والاستخدامات، يُرجى الاطّلاع على بنية CameraX.
  • Recorder هي تنفيذ "مخرجات الفيديو" إلى جانب VideoCapture. تُستخدَم Recorder لتسجيل الفيديو والصوت. إنّ ينشئ التطبيق تسجيلات من Recorder.
  • PendingRecording لإعداد التسجيل، وتوفير خيارات مثل تفعيل الصوت والإعدادات أداة معالجة الحدث. يجب استخدام Recorder لإنشاء PendingRecording. لا تسجِّل PendingRecording أي بيانات.
  • ينفذ Recording التسجيل الفعلي. يجب استخدام PendingRecording لإنشاء Recording.

يوضح الشكل 3 العلاقات بين هذه الكائنات:

رسم تخطيطي يوضح التفاعلات التي تحدث في مقطع فيديو
         التقاط حالة الاستخدام
الشكل 3. رسم بياني يوضح التفاعلات التي تحدث في حالة استخدام VideoCapture.

وسيلة الإيضاح:

  1. إنشاء Recorder باستخدام QualitySelector
  2. ضبط Recorder باستخدام إحدى OutputOptions
  3. تفعيل الصوت باستخدام withAudioEnabled() إذا لزم الأمر.
  4. الاتصال بالرقم start() مع VideoRecordEvent المستمع لبدء التسجيل.
  5. استخدام pause()/resume()/stop() في Recording للتحكم في التسجيل.
  6. الرد على VideoRecordEvents داخل أداة معالجة الحدث.

تتوفر قائمة واجهات برمجة التطبيقات التفصيلية في ملف current.txt داخل رمز المصدر.

استخدام واجهة برمجة تطبيقات VideoCapture

لدمج حالة استخدام CameraX VideoCapture في تطبيقك، يُرجى اتّباع الخطوات التالية: قم بما يلي:

  1. ربط VideoCapture.
  2. إعداد التسجيل وضبطه
  3. بدء التسجيل في بيئة التشغيل والتحكّم فيه

توضّح الأقسام التالية ما يمكنك فعله في كل خطوة للحصول على جلسة تسجيل شاملة.

ربط تسجيل الفيديو

لربط حالة الاستخدام VideoCapure، يمكنك اتّباع الخطوات التالية:

  1. أنشئ عنصر Recorder.
  2. إنشاء عنصر VideoCapture
  3. الربط بـ Lifecycle

تتبع واجهة برمجة تطبيقات CameraX VideoCapture نمط تصميم أداة الإنشاء. الطلبات استخدام Recorder.Builder لإنشاء Recorder يمكنك أيضًا ضبط درجة دقة الفيديو لـ Recorder من خلال كائن QualitySelector.

يتوافق CameraX Recorder مع ميزات Qualities المحددة مسبقًا التالية. لدرجات دقة الفيديو:

  • Quality.UHD لفيديو بدقة فائقة بتنسيق 4K (2160p)
  • Quality.FHD لمشاهدة الفيديو بالحجم الكامل بدقة عالية (1080p)
  • Quality.HD لحجم الفيديو بدقة عالية (720p)
  • Quality.SD لحجم الفيديو بدقة عادية (480p)

ويُرجى العِلم أنّه يمكن لتطبيق CameraX أيضًا اختيار درجات دقة أخرى إذا كان التطبيق يسمح بذلك.

يعتمد حجم الفيديو الدقيق لكل اختيار على الكاميرا وبرنامج الترميز والإمكانات. لمزيد من المعلومات، راجع وثائق CamcorderProfile

يمكن للتطبيقات ضبط درجة الدقة من خلال إنشاء QualitySelector يمكنك إنشاء QualitySelector باستخدام إحدى الطرق التالية:

  • تقديم بعض درجات الدقة المفضّلة باستخدام fromOrderedList() تضمين استراتيجية احتياطية لاستخدامها في حالة عدم وجود ودرجات الدقة المفضلة،

    يمكن لـ CameraX تحديد أفضل مطابقة احتياطية بناءً على عدسة الكاميرا المحددة يمكنك الرجوع إلى نظام FallbackStrategy specification الخاص بـ QualitySelector لمزيد من التفاصيل. على سبيل المثال، يطلب الرمز التالي أعلى مستوى دعم للتسجيل، وإذا لم يمكن دعم أي من حلول الطلبات، السماح لـ CameraX باختيار الدقة الأقرب إلى درجة دقة Quality.SD:

    val qualitySelector = QualitySelector.fromOrderedList(
             listOf(Quality.UHD, Quality.FHD, Quality.HD, Quality.SD),
             FallbackStrategy.lowerQualityOrHigherThan(Quality.SD))
    
  • يمكنك الاستعلام عن إمكانات الكاميرا أولاً، ثم الاختيار من بين درجات الدقة باستخدام QualitySelector::from():

    val cameraInfo = cameraProvider.availableCameraInfos.filter {
        Camera2CameraInfo
        .from(it)
        .getCameraCharacteristic(CameraCharacteristics.LENS\_FACING) == CameraMetadata.LENS_FACING_BACK
    }
    
    val supportedQualities = QualitySelector.getSupportedQualities(cameraInfo[0])
    val filteredQualities = arrayListOf (Quality.UHD, Quality.FHD, Quality.HD, Quality.SD)
                           .filter { supportedQualities.contains(it) }
    
    // Use a simple ListView with the id of simple_quality_list_view
    viewBinding.simpleQualityListView.apply {
        adapter = ArrayAdapter(context,
                               android.R.layout.simple_list_item_1,
                               filteredQualities.map { it.qualityToString() })
    
        // Set up the user interaction to manually show or hide the system UI.
        setOnItemClickListener { _, _, position, _ ->
            // Inside View.OnClickListener,
            // convert Quality.* constant to QualitySelector
            val qualitySelector = QualitySelector.from(filteredQualities[position])
    
            // Create a new Recorder/VideoCapture for the new quality
            // and bind to lifecycle
            val recorder = Recorder.Builder()
                .setQualitySelector(qualitySelector).build()
    
             // ...
        }
    }
    
    // A helper function to translate Quality to a string
    fun Quality.qualityToString() : String {
        return when (this) {
            Quality.UHD -> "UHD"
            Quality.FHD -> "FHD"
            Quality.HD -> "HD"
            Quality.SD -> "SD"
            else -> throw IllegalArgumentException()
        }
    }
    

    لاحظ أن الإمكانية التي تم إرجاعها من QualitySelector.getSupportedQualities() أن يعمل مع حالة الاستخدام VideoCapture أو الجمع بين حالة استخدام VideoCapture وPreview. عند الربط مع حالة الاستخدام "ImageCapture" أو "ImageAnalysis"، CameraX قد لا ينجح في الربط عندما لا تكون المجموعة المطلوبة متاحة على الكاميرا المطلوبة.

بعد الحصول على QualitySelector، يمكن للتطبيق إنشاء كائن VideoCapture ونفِّذ عملية الربط. لاحظ أن هذا الربط كما هو الحال مع حالات الاستخدام الأخرى:

val recorder = Recorder.Builder()
    .setExecutor(cameraExecutor).setQualitySelector(qualitySelector)
    .build()
val videoCapture = VideoCapture.withOutput(recorder)

try {
    // Bind use cases to camera
    cameraProvider.bindToLifecycle(
            this, CameraSelector.DEFAULT_BACK_CAMERA, preview, videoCapture)
} catch(exc: Exception) {
    Log.e(TAG, "Use case binding failed", exc)
}

يُرجى العِلم أنّ bindToLifecycle() تعرض الكائن Camera. راجِع هذا الدليل للحصول على مزيد من المعلومات حول التحكّم في إخراج الكاميرا، مثل التكبير أو التصغير والتعرض للضوء.

ويختار Recorder التنسيق الأنسب للنظام. الأكثر برنامج ترميز الفيديو الشائع H.264 AVC) مع تنسيق الحاوية MPEG-4:

إعداد التسجيل وإنشاؤه

من Recorder، يمكن للتطبيق إنشاء عناصر تسجيل إجراء التقاط الفيديو والصوت. تنشئ التطبيقات تسجيلات من خلال إجراء ما يلي:

  1. ضبط OutputOptions باستخدام prepareRecording()
  2. (اختياري) فعِّل التسجيل الصوتي.
  3. استخدام start() لتسجيل VideoRecordEvent المستمع والبدء في التقاط الفيديو.

تعرض Recorder الكائن Recording عند استدعاء الدالة start(). يمكن لتطبيقك استخدام عنصر Recording هذا لإكمال العملية. اتخاذ إجراءات أخرى أو تنفيذها، مثل الإيقاف المؤقت أو الاستئناف.

يتيح Recorder استخدام عنصر Recording واحد في كل مرة. يمكنك بدء تسجيلاً جديدًا بعد الاتصال بـ Recording.stop() أو Recording.close() في الكائن Recording السابق.

لنلقِ نظرة على هذه الخطوات بمزيد من التفصيل. أولاً، يقوم التطبيق بتهيئة OutputOptions لمسجّلة ذكية مع Recorder.prepareRecording(). تتوافق Recorder مع الأنواع التالية من OutputOptions:

  • FileDescriptorOutputOptions لالتقاط صورة FileDescriptor
  • FileOutputOptions لتسجيل فيديو في File
  • MediaStoreOutputOptions لالتقاط صورة MediaStore

تتيح لك جميع أنواع OutputOptions ضبط حد أقصى لحجم الملف باستخدام setFileSizeLimit() أمّا الخيارات الأخرى، تقتصر على النتائج الفردية النوع، مثل ParcelFileDescriptor للإشارة إلى FileDescriptorOutputOptions.

تعرض الدالة prepareRecording() الكائن PendingRecording، كائن وسيط يستخدم لإنشاء كائن Recording. PendingRecording هي فئة عابرة يجب تكون غير مرئية في معظم الحالات ونادرًا ما يتم تخزينها مؤقتًا بواسطة التطبيق.

يمكن للتطبيقات ضبط المزيد من إعدادات التسجيل، مثل:

  • تفعيل الصوت مع withAudioEnabled()
  • تسجيل أداة استماع لتلقّي أحداث تسجيلات الفيديو مع start(Executor, Consumer<VideoRecordEvent>).
  • السماح بتسجيل التسجيل باستمرار أثناء إرفاقه بـ VideoCapture بالعودة إلى كاميرا أخرى، باستخدام PendingRecording.asPersistentRecording()

لبدء التسجيل، اتصل برقم PendingRecording.start(). تقوم كاميرا X بتحويل PendingRecording إلى Recording، ويضيف طلب التسجيل إلى قائمة الانتظار، وتُرجع كائن Recording الذي تم إنشاؤه حديثًا إلى التطبيق. بعد بدء التسجيل على جهاز الكاميرا المقابل، ترسل CameraX حدث واحد (VideoRecordEvent.EVENT_TYPE_START).

يوضح المثال التالي كيفية تسجيل فيديو وصوت في ملف MediaStore:

// Create MediaStoreOutputOptions for our recorder
val name = "CameraX-recording-" +
        SimpleDateFormat(FILENAME_FORMAT, Locale.US)
                .format(System.currentTimeMillis()) + ".mp4"
val contentValues = ContentValues().apply {
   put(MediaStore.Video.Media.DISPLAY_NAME, name)
}
val mediaStoreOutput = MediaStoreOutputOptions.Builder(this.contentResolver,
                              MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
                              .setContentValues(contentValues)
                              .build()

// 2. Configure Recorder and Start recording to the mediaStoreOutput.
val recording = videoCapture.output
                .prepareRecording(context, mediaStoreOutput)
                .withAudioEnabled()
                .start(ContextCompat.getMainExecutor(this), captureListener)

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

هناك ثلاثة خيارات لـ MirrorMode: MIRROR_mode_OFF وMIRROR_mode_ON وMIRROR_mode_ON MIRROR_mode_ON_FRONT_ONLY. للمحاذاة مع معاينة الكاميرا، تنصح Google باستخدام MIROR_mode_ON_FRONT_ONLY، وهو ما يعني الذي/التي ميزة "المزامنة على الجهاز وفي السحابة الإلكترونية" غير مفعّلة للكاميرا الخلفية، ولكن مفعَّلة في الكاميرا الأمامية والكاميرا. لمزيد من المعلومات حول MirrorMode، يمكنك الاطلاع على MirrorMode constants

يعرض مقتطف الرمز هذا طريقة طلب VideoCapture.Builder.setMirrorMode() باستخدام MIRROR_MODE_ON_FRONT_ONLY بالنسبة للحصول على مزيد من المعلومات، يُرجى الاطّلاع على setMirrorMode().

Kotlin

val recorder = Recorder.Builder().build()

val videoCapture = VideoCapture.Builder(recorder)
    .setMirrorMode(MIRROR_MODE_ON_FRONT_ONLY)
    .build()

useCases.add(videoCapture);

Java

Recorder.Builder builder = new Recorder.Builder();
if (mVideoQuality != QUALITY_AUTO) {
    builder.setQualitySelector(
        QualitySelector.from(mVideoQuality));
}
  VideoCaptureR<ecorder >videoCapture = new VideoCapture.Builder(<>builder.build())
      .setMirrorMode(MIRROR_MODE_ON_FRONT_ONLY)
      .build();
    useCases.add(videoCapture);

التحكّم في تسجيل نشط

يمكنك إيقاف Recording الحالية مؤقتًا واستئنافها وإيقافها بحلول باستخدام الطرق التالية:

  • pause لإيقاف التسجيل النشِط الحالي مؤقتًا.
  • resume() لاستئناف تسجيل نشط تم إيقافه مؤقتًا.
  • stop() لإنهاء التسجيل ومسح أي عناصر تسجيل مرتبطة.
  • mute() لكتم صوت التسجيل الحالي أو إعادته

تجدر الإشارة إلى أنّه يمكنك الاتصال بـ stop() لإنهاء Recording بغض النظر عن ذلك. لتحديد ما إذا كان التسجيل في حالة إيقاف مؤقت أو نشط.

إذا سجّلت نطاق EventListener من خلال PendingRecording.start()، تتواصل Recording باستخدام VideoRecordEvent

  • تُستخدم السمة VideoRecordEvent.EVENT_TYPE_STATUS لتسجيل إحصاءات مثل بحجم الملف الحالي والفترة الزمنية المسجّلة.
  • يتم استخدام VideoRecordEvent.EVENT_TYPE_FINALIZE لنتيجة التسجيل. وتتضمن معلومات مثل عنوان URI للملف النهائي مع أي أخطاء ذات صلة.

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

مصادر إضافية

للتعرف على مزيد من المعلومات حول CameraX، يمكنك الاطلاع على الموارد الإضافية التالية: