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

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

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

در CameraX، راه حل برای فیلم برداری، استفاده از VideoCapture است:

نمودار مفهومی که نشان می‌دهد دوربین x چگونه از حالت استفاده از فیلمبرداری استفاده می‌کند
شکل 2. نمودار مفهومی که نشان می‌دهد CameraX چگونه از VideoCapture استفاده می‌کند.

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

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

VideoCapture API موتور پیچیده تصویربرداری را خلاصه می کند و برنامه های کاربردی را با API بسیار ساده تر و ساده تر ارائه می دهد.

نمای کلی VideoCapture API

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

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

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

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

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

افسانه:

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

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

با استفاده از VideoCapture API

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

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

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

Bind VideoCapture

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

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

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

CameraX Recorder از 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. OutputOptions را با prepareRecording() پیکربندی کنید.
  2. (اختیاری) ضبط صدا را فعال کنید.
  3. از start() برای ثبت شنونده VideoRecordEvent و شروع فیلمبرداری استفاده کنید.

هنگامی که تابع start() را فراخوانی می کنید، Recorder یک شی Recording برمی گرداند. برنامه شما می تواند از این شیء 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() را فراخوانی کنید. 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. برای تراز کردن با پیش‌نمایش دوربین، Google استفاده از 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));
}
  VideoCaptureR<ecorder >videoCapture = new VideoCapture.Builder(<>builder.build())
      .setMirrorMode(MIRROR_MODE_ON_FRONT_ONLY)
      .build();
    useCases.add(videoCapture);

یک ضبط فعال را کنترل کنید

با استفاده از روش‌های زیر می‌توانید Recording مداوم را متوقف کنید، از سر بگیرید و متوقف کنید:

  • pause برای توقف ضبط فعال فعلی.
  • resume() برای از سرگیری ضبط فعال متوقف شده.
  • stop() برای پایان دادن به ضبط و شستشوی اشیاء ضبط مرتبط.
  • mute() برای بی صدا کردن یا لغو صدای ضبط فعلی.

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

اگر یک EventListener با PendingRecording.start() ثبت کرده باشید، Recording با استفاده از VideoRecordEvent ارتباط برقرار می کند.

  • VideoRecordEvent.EVENT_TYPE_STATUS برای ضبط آماری مانند اندازه فایل فعلی و بازه زمانی ثبت شده استفاده می شود.
  • VideoRecordEvent.EVENT_TYPE_FINALIZE برای نتیجه ضبط استفاده می شود و شامل اطلاعاتی مانند URI فایل نهایی به همراه هرگونه خطای مرتبط است.

هنگامی که برنامه شما یک EVENT_TYPE_FINALIZE دریافت کرد که نشان دهنده یک جلسه ضبط موفقیت آمیز است، سپس می توانید از مکان مشخص شده در OutputOptions به ویدیوی ضبط شده دسترسی داشته باشید.

منابع اضافی

برای کسب اطلاعات بیشتر در مورد CameraX، به منابع اضافی زیر مراجعه کنید: