اگر برنامه شما از کلاس Camera
اصلی ("Camera1") استفاده می کند که از Android نسخه 5.0 (سطح API 21) منسوخ شده است، به شدت توصیه می کنیم به یک API دوربین Android مدرن به روز رسانی کنید. Android CameraX (یک API استاندارد و قوی دوربین Jetpack ) و Camera2 (یک API سطح پایین چارچوب) را ارائه می دهد. در اکثر موارد، توصیه می کنیم برنامه خود را به CameraX منتقل کنید. در اینجا دلیل آن است:
- سهولت استفاده: CameraX جزئیات سطح پایین را کنترل می کند، به طوری که می توانید کمتر روی ایجاد یک تجربه دوربین از ابتدا تمرکز کنید و بیشتر روی متمایز کردن برنامه خود تمرکز کنید.
- CameraX تکه تکه شدن را برای شما کنترل می کند: CameraX هزینه های نگهداری طولانی مدت و کدهای خاص دستگاه را کاهش می دهد و تجربه های با کیفیت بالاتری را برای کاربران به ارمغان می آورد. برای اطلاعات بیشتر در این مورد، پست وبلاگ سازگاری بهتر دستگاه با CameraX را بررسی کنید.
- قابلیتهای پیشرفته: CameraX با دقت طراحی شده است تا قابلیتهای پیشرفته را در برنامه شما ساده کند. برای مثال، میتوانید به راحتی بوکه، روتوش چهره، HDR (محدوده دینامیکی بالا) و حالت عکاسی در شب با روشنایی کم را روی عکسهای خود با برنامههای افزودنی CameraX اعمال کنید.
- قابلیت به روز رسانی: اندروید قابلیت های جدید و رفع اشکالات را در CameraX در طول سال منتشر می کند. با مهاجرت به CameraX، برنامه شما آخرین فناوری دوربین Android را با هر نسخه CameraX دریافت میکند، نه فقط در نسخههای سالانه Android.
در این راهنما، سناریوهای رایج برای برنامه های دوربین را خواهید یافت. هر سناریو شامل اجرای Camera1 و اجرای CameraX برای مقایسه است.
وقتی صحبت از مهاجرت به میان میآید، گاهی اوقات برای ادغام با یک پایگاه کد موجود به انعطافپذیری بیشتری نیاز دارید. تمام کدهای CameraX در این راهنما دارای یک اجرای CameraController
هستند - اگر میخواهید سادهترین راه را برای استفاده از CameraX داشته باشید - و همچنین اجرای CameraProvider
- عالی است اگر به انعطافپذیری بیشتری نیاز دارید. برای کمک به شما در تصمیم گیری اینکه کدام یک برای شما مناسب است، در اینجا مزایای هر کدام آورده شده است:
دوربین کنترلر | Camera Provider |
به کد راه اندازی کمی نیاز دارد | امکان کنترل بیشتر را فراهم می کند |
اجازه دادن به CameraX برای انجام بیشتر مراحل راهاندازی به این معنی است که عملکردهایی مانند فوکوس ضربه زدن و زوم کردن بهطور خودکار کار میکنند. | از آنجایی که توسعهدهنده برنامه تنظیمات را انجام میدهد، فرصتهای بیشتری برای سفارشی کردن پیکربندی وجود دارد، مانند فعال کردن چرخش تصویر خروجی یا تنظیم فرمت تصویر خروجی در ImageAnalysis |
نیاز به PreviewView برای پیشنمایش دوربین به CameraX اجازه میدهد تا یکپارچهسازی یکپارچه سرتاسر را ارائه دهد، مانند ادغام کیت ML ما که میتواند مختصات نتیجه مدل ML (مانند جعبههای محدودکننده چهره) را مستقیماً روی مختصات پیشنمایش ترسیم کند. | امکان استفاده از «Surface» سفارشی برای پیشنمایش دوربین، به انعطافپذیری بیشتری اجازه میدهد، مانند استفاده از کد «Surface» موجود شما که میتواند ورودی به بخشهای دیگر برنامه شما باشد. |
اگر در تلاش برای مهاجرت گیر کردید، در گروه بحث CameraX با ما تماس بگیرید.
قبل از مهاجرت
استفاده از CameraX را با Camera1 مقایسه کنید
در حالی که ممکن است کد متفاوت به نظر برسد، مفاهیم اساسی در Camera1 و CameraX بسیار شبیه هستند. CameraX عملکردهای رایج دوربین را در موارد استفاده خلاصه میکند و در نتیجه، بسیاری از وظایفی که در Camera1 به توسعهدهنده واگذار شده بود، بهطور خودکار توسط CameraX انجام میشود. چهار UseCase
در CameraX وجود دارد که میتوانید از آنها برای کارهای مختلف دوربین استفاده کنید: Preview
، ImageCapture
، VideoCapture
و ImageAnalysis
.
یک نمونه از CameraX که جزئیات سطح پایین را برای توسعه دهندگان مدیریت می کند، ViewPort
است که بین UseCase
های فعال به اشتراک گذاشته شده است. این تضمین میکند که همه UseCase
پیکسلهای مشابهی را ببینند. در Camera1، شما باید خودتان این جزئیات را مدیریت کنید، و با توجه به تنوع نسبتهای تصویر در حسگرها و صفحهنمایشهای دوربین دستگاهها، اطمینان از اینکه پیشنمایش شما با عکسها و ویدیوهای گرفته شده مطابقت دارد، میتواند مشکل باشد.
به عنوان مثالی دیگر، CameraX تماسهای Lifecycle
را بهطور خودکار در نمونه Lifecycle
که از آن عبور میکنید، کنترل میکند. این بدان معناست که CameraX اتصال برنامه شما به دوربین را در طول چرخه حیات Android کنترل میکند، از جمله موارد زیر: بستن دوربین زمانی که برنامه شما به پسزمینه میرود. حذف پیش نمایش دوربین زمانی که صفحه دیگر نیازی به نمایش آن ندارد. و توقف پیشنمایش دوربین هنگامی که فعالیت دیگری در پیشزمینه اولویت دارد، مانند تماس ویدیویی ورودی.
در نهایت، CameraX چرخش و مقیاس بندی را بدون نیاز به کد اضافی از جانب شما انجام می دهد. در مورد یک Activity
با جهت قفل نشده، هر بار که دستگاه چرخانده می شود، تنظیم UseCase
انجام می شود، زیرا سیستم باعث از بین رفتن و ایجاد مجدد Activity
در تغییرات جهت گیری می شود. این باعث می شود که UseCases
چرخش هدف خود را هر بار به طور پیش فرض مطابق با جهت نمایشگر تنظیم کند. درباره چرخش در CameraX بیشتر بخوانید .
قبل از پرداختن به جزئیات، در اینجا نگاهی سطح بالا به UseCase
s CameraX و نحوه ارتباط یک برنامه Camera1 میدهیم. (مفاهیم CameraX به رنگ آبی و مفاهیم Camera1 به رنگ سبز هستند.)
CameraX | |||
CameraController / پیکربندی CameraProvider | |||
↓ | ↓ | ↓ | ↓ |
پیش نمایش | ImageCapture | فیلمبرداری | تجزیه و تحلیل تصویر |
⁞ | ⁞ | ⁞ | ⁞ |
پیش نمایش سطح را مدیریت کنید و آن را روی دوربین تنظیم کنید | PictureCallback را تنظیم کنید و takePicture() را روی دوربین فراخوانی کنید | پیکربندی دوربین و MediaRecorder را به ترتیب خاصی مدیریت کنید | کد تجزیه و تحلیل سفارشی ساخته شده در بالای سطح پیش نمایش |
↑ | ↑ | ↑ | ↑ |
کد مخصوص دستگاه | |||
↑ | |||
چرخش دستگاه و مدیریت مقیاس | |||
↑ | |||
مدیریت جلسه دوربین (انتخاب دوربین، مدیریت چرخه زندگی) | |||
دوربین 1 |
سازگاری و عملکرد در CameraX
CameraX از دستگاه های دارای Android نسخه 5.0 (سطح API 21) و بالاتر پشتیبانی می کند. این بیش از 98٪ از دستگاه های اندروید موجود را نشان می دهد. CameraX برای کنترل خودکار تفاوتهای بین دستگاهها ساخته شده است و نیاز به کدهای خاص دستگاه را در برنامه شما کاهش میدهد. علاوه بر این، ما بیش از 150 دستگاه فیزیکی را در همه نسخههای Android از نسخه 5.0 در آزمایشگاه تست CameraX خود آزمایش میکنیم. میتوانید فهرست کامل دستگاههایی را که در حال حاضر در آزمایشگاه تست هستند، مرور کنید.
CameraX از یک Executor
برای هدایت پشته دوربین استفاده می کند. در صورتی که برنامه شما نیاز به رشته خاصی دارد، میتوانید مجری خود را در CameraX تنظیم کنید . اگر تنظیم نشود، CameraX یک Executor
داخلی پیشفرض بهینهسازی شده ایجاد و استفاده میکند. بسیاری از APIهای پلتفرمی که CameraX بر روی آنها ساخته شده است نیاز به مسدود کردن ارتباطات بین پردازشی (IPC) با سخت افزار دارند که گاهی اوقات ممکن است صدها میلی ثانیه طول بکشد تا پاسخ دهند. به همین دلیل، CameraX فقط این APIها را از رشتههای پسزمینه فراخوانی میکند، که تضمین میکند رشته اصلی مسدود نشده است و رابط کاربری روان باقی میماند. در مورد رشته ها بیشتر بخوانید .
اگر بازار هدف برنامه شما شامل دستگاههای ارزانقیمت است، CameraX راهی برای کاهش زمان راهاندازی با محدودکننده دوربین ارائه میکند. از آنجایی که فرآیند اتصال به اجزای سختافزاری، بهویژه در دستگاههای ارزانقیمت میتواند زمان بیاهمیتی را ببرد، میتوانید مجموعه دوربینهای مورد نیاز برنامه خود را مشخص کنید. CameraX فقط در حین راه اندازی به این دوربین ها متصل می شود. برای مثال، اگر برنامه فقط از دوربینهای پشتی استفاده میکند، میتواند این پیکربندی را با DEFAULT_BACK_CAMERA
تنظیم کند و سپس CameraX از تنظیم اولیه دوربینهای جلو برای کاهش تأخیر جلوگیری میکند.
مفاهیم توسعه اندروید
این راهنما آشنایی کلی با توسعه اندروید را فرض می کند. فراتر از اصول اولیه، در اینجا چند مفهوم وجود دارد که درک آنها قبل از پرش به کد زیر مفید است:
- View Binding یک کلاس اتصال برای فایلهای طرحبندی XML شما ایجاد میکند، که به شما امکان میدهد به راحتی به نماهای خود در Activities ارجاع دهید ، همانطور که در چند قطعه کد زیر انجام میشود. بین view binding و
findViewById()
(روش قبلی برای ارجاع به view ها) تفاوت هایی وجود دارد، اما در کد زیر باید بتوانید خطوط view binding را با فراخوانیfindViewById()
مشابه جایگزین کنید. - Coroutine های ناهمزمان یک الگوی طراحی همزمان هستند که در 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()
بدون هیچ پارامتری برای باز کردن اولین دوربین پشتی فراخوانی کنید، یا میتوانید یک ID عدد صحیح برای دوربینی که میخواهید باز کنید ارسال کنید. در اینجا مثالی از نحوه ظاهر آن آمده است:
// 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
انجام می شود. CameraX استفاده از دوربین پیش فرض را آسان می کند. میتوانید مشخص کنید که دوربین جلوی پیشفرض یا دوربین پشتی پیشفرض را میخواهید. علاوه بر این، شی 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() } }
اگر میخواهید روی دوربینی که انتخاب میشود کنترل کنید، اگر از CameraProvider
استفاده میکنید با فراخوانی getAvailableCameraInfos()
که یک شی CameraInfo
برای بررسی ویژگیهای دوربین مانند isFocusMeteringSupported()
به شما میدهد، در CameraX نیز امکانپذیر است. سپس می توانید آن را به 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()
نمایش پیش نمایش
اکثر برنامههای دوربین باید در نقطهای فید دوربین را روی صفحه نمایش دهند. با Camera1، باید تماس های چرخه حیات را به درستی مدیریت کنید، همچنین باید چرخش و مقیاس را برای پیش نمایش خود تعیین کنید.
علاوه بر این، در Camera1 باید تصمیم بگیرید که از TextureView
یا SurfaceView
به عنوان سطح پیش نمایش خود استفاده کنید. هر دو گزینه با معاوضه هایی همراه هستند، و در هر صورت، Camera1 از شما می خواهد که چرخش و مقیاس بندی را به درستی مدیریت کنید. از سوی دیگر، PreviewView
CameraX دارای پیاده سازی های اساسی برای TextureView
و SurfaceView
است. CameraX بسته به عواملی مانند نوع دستگاه و نسخه اندرویدی که برنامه شما روی آن اجرا می شود، تصمیم می گیرد که کدام پیاده سازی بهترین است. اگر هر یک از پیاده سازی ها سازگار است، می توانید اولویت خود را با 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
استفاده کنید، اما همچنان تنظیمات پیش نمایش را در Camera1 بسیار ساده می کند. برای اهداف نمایشی، این مثال از یک PreviewView
استفاده میکند، اما اگر نیازهای پیچیدهتری دارید، میتوانید یک SurfaceProvider
سفارشی بنویسید تا به setSurfaceProvider()
منتقل شود.
در اینجا، Preview
UseCase
مانند 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
برای پیاده سازی tap-to-focus در Camera1، باید 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()
tap-to-focus را فعال و غیرفعال کنید و مقدار را با دریافتکننده مربوطه 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
دو راه برای بزرگنمایی با استفاده از Camera1 وجود دارد. متد Camera.startSmoothZoom()
از سطح زوم فعلی به سطح بزرگنمایی که در آن عبور می کنید متحرک می شود. روش Camera.Parameters.setZoom()
مستقیماً به سطح بزرگنمایی می رود. قبل از استفاده از یکی، isSmoothZoomSupported()
یا فراخوانی کنید. isZoomSupported()
به ترتیب، برای اطمینان از اینکه روشهای بزرگنمایی مرتبطی که نیاز دارید در دوربین شما موجود است.
برای پیادهسازی pinch-to-zoom، این مثال از 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
مانند تپ برای فوکوس، به رویدادهای لمسی PreviewView گوش میدهد تا به طور خودکار زوم کردن را انجام دهد. میتوانید pinch-to-zoom را با 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
دریافت کنید، جایی که با فراخوانیbindToLifecycle()
نمونهCamera
برگردانده می شود. - اگر
ZoomState
یک مقدارzoomRatio
دارد، آن را به عنوان نسبت بزرگنمایی فعلی ذخیره کنید. اگر درZoomState
هیچzoomRatio
وجود ندارد، از نرخ بزرگنمایی پیشفرض دوربین (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 با پیاده سازی یک متد takePicture()
خود، سادگی Camera1 را برای گرفتن تصویر حفظ می کند. در اینجا، تابعی را برای پیکربندی ورودی 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()
را فراخوانی کنید. برای مثال کامل تابع takePhoto()
کد CameraController
را در این بخش ببینید.
// 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
ضبط ویدیو با استفاده از Camera1 به مدیریت دقیق Camera
و MediaRecorder
نیاز دارد و روشها باید به ترتیب خاصی فراخوانی شوند. شما باید این ترتیب را دنبال کنید تا برنامه شما به درستی کار کند:
- دوربین را باز کنید.
- یک پیشنمایش را آماده و شروع کنید (اگر برنامه شما ویدیوی در حال ضبط را نشان میدهد، که معمولاً همینطور است).
- با فراخوانی
Camera.unlock()
قفل دوربین را برای استفادهMediaRecorder
باز کنید. - ضبط را با فراخوانی این روش ها در
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)
تماس بگیرید. - برای نهایی کردن پیکربندی
MediaRecorder
خودprepare()
فراخوانی کنید.
- نمونه
- برای شروع ضبط،
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
با CameraX's CameraController
، میتوانید ImageCapture
، VideoCapture
، و ImageAnalysis
UseCase
را بهطور مستقل تغییر دهید، تا زمانی که فهرست UseCaseها به طور همزمان قابل استفاده باشد . ImageCapture
و ImageAnalysis
UseCase
به طور پیش فرض فعال هستند، به همین دلیل است که برای گرفتن عکس نیازی به فراخوانی 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
را با UseCase
دیگر خود به CameraProvider
متصل کنید.
// 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
، چندین روش برای فراخوانی برای آماده سازی آن وجود دارد. برای تنظیم گزینه های خروجی MediaStore
prepareRecording()
فراخوانی کنید. اگر برنامه شما اجازه استفاده از میکروفون دستگاه را دارد، 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 در مخزن Camera Samples GitHub خود داریم. این نمونهها به شما نشان میدهند که چگونه سناریوهای موجود در این راهنما در یک برنامه اندرویدی کامل قرار میگیرند.
اگر برای مهاجرت به CameraX پشتیبانی بیشتری میخواهید یا در مورد مجموعه APIهای دوربین Android سؤالی دارید، لطفاً در گروه بحث CameraX با ما تماس بگیرید.