معماری فیلمبرداری CameraX

یک سیستم ضبط معمولاً جریان‌های ویدیویی و صوتی را ضبط می‌کند، آنها را فشرده می‌کند، دو جریان را با هم ترکیب می‌کند و سپس جریان حاصل را روی دیسک می‌نویسد.

نمودار مفهومی برای یک سیستم ضبط ویدئو و صدا
شکل ۱. نمودار مفهومی برای یک سیستم ضبط ویدئو و صدا.

در CameraX، راهکار ضبط ویدیو، مورد استفاده VideoCapture است:

نمودار مفهومی که نشان می‌دهد دوربین x چگونه مورد استفاده ضبط ویدیو را مدیریت می‌کند
شکل ۲. نمودار مفهومی که نشان می‌دهد CameraX چگونه مورد استفاده VideoCapture را مدیریت می‌کند.

همانطور که در شکل 2 نشان داده شده است، ضبط ویدیوی CameraX شامل چند مؤلفه معماری سطح بالا است:

  • SurfaceProvider برای منبع ویدیو.
  • AudioSource برای منبع صوتی.
  • دو انکودر برای انکود و فشرده‌سازی ویدیو/صدا.
  • یک میکسر رسانه‌ای برای ترکیب دو جریان.
  • یک فایل سیور برای نوشتن نتیجه.

رابط برنامه‌نویسی کاربردی (API) ضبط ویدیو، موتور ضبط پیچیده را کنار گذاشته و یک رابط برنامه‌نویسی کاربردی (API) بسیار ساده‌تر و سرراست‌تر را در اختیار برنامه‌ها قرار می‌دهد.

مرور کلی API ضبط ویدیو

VideoCapture یک مورد استفاده CameraX است که به تنهایی یا در ترکیب با سایر موارد استفاده به خوبی کار می‌کند. ترکیب‌های پشتیبانی‌شده خاص به قابلیت‌های سخت‌افزاری دوربین بستگی دارند، اما Preview و VideoCapture یک ترکیب مورد استفاده معتبر در همه دستگاه‌ها هستند.

رابط برنامه‌نویسی کاربردی (API) ضبط ویدیو (VideoCapture) شامل اشیاء زیر است که با برنامه‌ها ارتباط برقرار می‌کنند:

  • VideoCapture کلاس مورد استفاده سطح بالا است. VideoCapture به یک LifecycleOwner با یک CameraSelector و سایر UseCases های CameraX متصل می‌شود. برای اطلاعات بیشتر در مورد این مفاهیم و کاربردها، به معماری CameraX مراجعه کنید.
  • Recorder یک پیاده‌سازی از VideoOutput است که به طور تنگاتنگی با VideoCapture جفت شده است. Recorder برای انجام ضبط ویدیو و صدا استفاده می‌شود. یک برنامه، ضبط‌ها را از Recorder ایجاد می‌کند .
  • یک PendingRecording یک ضبط را پیکربندی می‌کند و گزینه‌هایی مانند فعال کردن صدا و تنظیم شنونده رویداد را ارائه می‌دهد. برای ایجاد یک PendingRecording باید از یک Recorder استفاده کنید. PendingRecording چیزی را ضبط نمی‌کند.
  • یک Recording ضبط واقعی را انجام می‌دهد. برای ایجاد یک Recording باید از PendingRecording استفاده کنید.

شکل ۳ روابط بین این اشیاء را نشان می‌دهد:

نموداری که تعاملات رخ داده در یک مورد استفاده از ضبط ویدیو را نشان می‌دهد
شکل ۳. نموداری که تعاملات رخ داده در یک مورد استفاده از VideoCapture را نشان می‌دهد.

افسانه:

  1. با QualitySelector یک Recorder ایجاد کنید.
  2. Recorder با یکی از OutputOptions پیکربندی کنید.
  3. در صورت نیاز، صدا را با withAudioEnabled() فعال کنید.
  4. برای شروع ضبط، تابع start() را با یک شنونده‌ی VideoRecordEvent فراخوانی کنید.
  5. برای کنترل ضبط، pause() / resume() / stop() در Recording استفاده کنید.
  6. به VideoRecordEvents درون شنونده رویداد خود پاسخ دهید.

لیست دقیق API در فایل current.txt درون کد منبع قرار دارد.

استفاده از API ضبط ویدیو

برای ادغام مورد استفاده CameraX VideoCapture در برنامه خود، موارد زیر را انجام دهید:

  1. VideoCapture مقید کنید.
  2. آماده سازی و پیکربندی ضبط.
  3. ضبط در زمان اجرا را شروع و کنترل کنید.

بخش‌های زیر شرح می‌دهند که در هر مرحله چه کاری می‌توانید انجام دهید تا یک جلسه ضبط سرتاسری داشته باشید.

ضبط ویدیو را متصل کنید

برای اتصال مورد استفاده VideoCapture ، موارد زیر را انجام دهید:

  1. یک شیء Recorder ایجاد کنید.
  2. شیء VideoCapture را ایجاد کنید.
  3. اتصال به یک Lifecycle .

رابط برنامه‌نویسی کاربردی CameraX VideoCapture از الگوی طراحی سازنده پیروی می‌کند. برنامه‌ها Recorder.Builder برای ایجاد یک Recorder استفاده می‌کنند. همچنین می‌توانید وضوح تصویر را برای Recorder از طریق یک شیء QualitySelector پیکربندی کنید.

Recorder CameraX از Qualities از پیش تعریف شده زیر برای وضوح تصویر ویدیو پشتیبانی می‌کند:

  • Quality.UHD برای اندازه ویدیوی 4K ultra HD (2160p)
  • Quality.FHD برای اندازه ویدیوی فول اچ‌دی (1080p)
  • Quality.HD برای اندازه ویدیوی HD (720p)
  • Quality.SD برای اندازه ویدیوی 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. با استفاده از تابع prepareRecording() OutputOptions ) را پیکربندی کنید.
  2. (اختیاری) ضبط صدا را فعال کنید.
  3. start() برای ثبت یک شنونده VideoRecordEvent استفاده کنید و ضبط ویدیو را شروع کنید.

Recorder هنگام فراخوانی تابع start() یک شیء Recording object) برمی‌گرداند. برنامه شما می‌تواند از این شیء Recording برای اتمام ضبط یا انجام اقدامات دیگر، مانند مکث یا از سرگیری، استفاده کند.

یک Recorder در هر زمان از یک شیء Recording پشتیبانی می‌کند. می‌توانید پس از فراخوانی Recording.stop() یا Recording.close() روی شیء Recording قبلی، یک ضبط جدید را شروع کنید.

بیایید این مراحل را با جزئیات بیشتری بررسی کنیم. ابتدا، برنامه، OutputOptions را برای یک Recorder با Recorder.prepareRecording() پیکربندی می‌کند. یک Recorder از انواع OutputOptions زیر پشتیبانی می‌کند:

  • FileDescriptorOutputOptions برای ثبت در یک FileDescriptor .
  • FileOutputOptions برای ضبط در یک File .
  • MediaStoreOutputOptions برای ضبط در MediaStore .

همه انواع OutputOptions به شما امکان می‌دهند حداکثر اندازه فایل را با setFileSizeLimit() تنظیم کنید. گزینه‌های دیگر مختص نوع خروجی خاص هستند، مانند ParcelFileDescriptor برای FileDescriptorOutputOptions .

prepareRecording() یک شیء PendingRecording برمی‌گرداند که یک شیء میانی است و برای ایجاد شیء Recording مربوطه استفاده می‌شود. PendingRecording یک کلاس گذرا است که در بیشتر موارد باید نامرئی باشد و به ندرت توسط برنامه ذخیره می‌شود.

برنامه‌ها می‌توانند ضبط را بیشتر پیکربندی کنند، مانند:

  • صدا را با استفاده از withAudioEnabled() فعال کنید.
  • یک شنونده (listener) ثبت کنید تا رویدادهای ضبط ویدیو را با start(Executor, Consumer<VideoRecordEvent>) دریافت کند.
  • با استفاده از PendingRecording.asPersistentRecording() ‎، به یک ضبط اجازه دهید تا به طور مداوم ضبط کند در حالی که VideoCapture که به آن متصل است به دوربین دیگری منتقل می‌شود.

برای شروع ضبط، PendingRecording.start() را فراخوانی کنید. CameraX، 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_FRONT_ONLY. برای تراز کردن با پیش‌نمایش دوربین، گوگل استفاده از MIROR_MODE_ON_FRONT_ONLY را توصیه می‌کند، به این معنی که آینه‌سازی برای دوربین عقب فعال نیست، اما برای دوربین جلو فعال است. برای اطلاعات بیشتر در مورد MirrorMode، به MirrorMode constants مراجعه کنید.

این قطعه کد نحوه فراخوانی VideoCapture.Builder.setMirrorMode() را با استفاده از MIRROR_MODE_ON_FRONT_ONLY نشان می‌دهد. برای اطلاعات بیشتر، به setMirrorMode() مراجعه کنید.

کاتلین

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

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

useCases.add(videoCapture);

جاوا

Recorder.Builder builder = new Recorder.Builder();
if (mVideoQuality != QUALITY_AUTO) {
    builder.setQualitySelector(
        QualitySelector.from(mVideoQuality));
}
  VideoCapture<Recorder> 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، به منابع اضافی زیر مراجعه کنید: