अगर आपका ऐप्लिकेशन, Camera क्लास ("Camera1") का इस्तेमाल करता है, तो हमारा सुझाव है कि आप Android के नए कैमरा एपीआई पर अपडेट करें. इस क्लास का इस्तेमाल Android 5.0 (एपीआई लेवल 21) के बाद से बंद कर दिया गया है. Android, CameraX (एक स्टैंडर्ड, भरोसेमंद Jetpack कैमरा एपीआई) और Camera2 (एक लो-लेवल, फ़्रेमवर्क एपीआई) उपलब्ध कराता है.
ज़्यादातर मामलों में, हमारा सुझाव है कि आप अपने ऐप्लिकेशन को CameraX पर माइग्रेट करें. यहां बताया गया है कि क्यों:
- इस्तेमाल में आसान: CameraX, लो-लेवल की जानकारी को मैनेज करता है. इससे आपको कैमरा ऐप्लिकेशन को शुरू से बनाने पर कम ध्यान देना पड़ता है. साथ ही, अपने ऐप्लिकेशन को बेहतर बनाने पर ज़्यादा ध्यान दिया जा सकता है.
- CameraX, फ़्रैगमेंटेशन को मैनेज करता है: CameraX, लंबे समय तक रखरखाव में लगने वाले खर्च और डिवाइस के हिसाब से कोड को कम करता है. इससे लोगों को बेहतर क्वालिटी का अनुभव मिलता है. इस बारे में ज़्यादा जानने के लिए, हमारी CameraX के साथ डिवाइसों की बेहतर कंपैटिबिलिटी ब्लॉग पोस्ट पढ़ें.
- ऐडवांस सुविधाएं: CameraX को इस तरह से डिज़ाइन किया गया है कि ऐडवांस सुविधाओं को आपके ऐप्लिकेशन में आसानी से शामिल किया जा सके. उदाहरण के लिए, CameraX Extensions की मदद से, अपनी फ़ोटो पर आसानी से बोके इफ़ेक्ट, चेहरे को बेहतर बनाने की सुविधा, एचडीआर (हाई डाइनैमिक रेंज) और कम रोशनी में फ़ोटो को बेहतर बनाने वाला नाइट मोड लागू किया जा सकता है.
- अपडेट करने की सुविधा: Android, साल भर में CameraX के लिए नई सुविधाएं और गड़बड़ियां ठीक करने वाले अपडेट रिलीज़ करता है. CameraX पर माइग्रेट करने से, आपके ऐप्लिकेशन को CameraX के हर वर्शन के साथ Android की नई कैमरा टेक्नोलॉजी मिलती है. यह सिर्फ़ Android के सालाना वर्शन रिलीज़ के साथ नहीं मिलती.
इस गाइड में, आपको कैमरा ऐप्लिकेशन के लिए सामान्य स्थितियां मिलेंगी. हर सिनारियो में, तुलना करने के लिए Camera1 और CameraX, दोनों का इस्तेमाल किया गया है.
माइग्रेशन के दौरान, कभी-कभी आपको मौजूदा कोडबेस के साथ इंटिग्रेट करने के लिए ज़्यादा फ़्लेक्सिबिलिटी की ज़रूरत होती है. इस गाइड में दिए गए सभी CameraX कोड में CameraController लागू किया गया है. अगर आपको CameraX का इस्तेमाल आसान तरीके से करना है, तो यह आपके लिए बहुत अच्छा है. साथ ही, इसमें CameraProvider भी लागू किया गया है. अगर आपको ज़्यादा फ़्लेक्सिबिलिटी चाहिए, तो यह आपके लिए बहुत अच्छा है. आपके लिए कौनसा ऐड-ऑन सही है, यह तय करने में आपकी मदद करने के लिए, यहां हर ऐड-ऑन के फ़ायदे दिए गए हैं:
CameraController |
CameraProvider |
| इसके लिए, कम सेटअप कोड की ज़रूरत होती है | ज़्यादा कंट्रोल की अनुमति देता है |
| CameraX को सेटअप की ज़्यादा प्रोसेस मैनेज करने की अनुमति देने का मतलब है कि टैप करके फ़ोकस करने और पिंच करके ज़ूम करने जैसी सुविधाएं अपने-आप काम करती हैं |
ऐप्लिकेशन डेवलपर, सेटअप को मैनेज करता है. इसलिए, कॉन्फ़िगरेशन को पसंद के मुताबिक बनाने के ज़्यादा मौके मिलते हैं. जैसे, आउटपुट इमेज रोटेशन की सुविधा चालू करना या ImageAnalysis में आउटपुट इमेज का फ़ॉर्मैट सेट करना
|
कैमरे की झलक के लिए PreviewView की ज़रूरत होती है. इससे CameraX को एंड-टू-एंड इंटिग्रेशन की सुविधा मिलती है. जैसे, ML Kit इंटिग्रेशन में, एमएल मॉडल के नतीजे के कोऑर्डिनेट (जैसे, चेहरे के बाउंडिंग बॉक्स) को सीधे तौर पर झलक के कोऑर्डिनेट पर मैप किया जा सकता है
|
कैमरे की झलक के लिए कस्टम `Surface` का इस्तेमाल करने की सुविधा से, ज़्यादा फ़्लेक्सिबिलिटी मिलती है. जैसे, अपने मौजूदा `Surface` कोड का इस्तेमाल करना. यह कोड, आपके ऐप्लिकेशन के अन्य हिस्सों के लिए इनपुट हो सकता है |
अगर आपको माइग्रेट करने में समस्या आ रही है, तो CameraX Discussion Group पर हमसे संपर्क करें.
माइग्रेट करने से पहले
CameraX और Camera1 के इस्तेमाल की तुलना करना
कोड अलग-अलग दिख सकता है, लेकिन Camera1 और CameraX के बुनियादी सिद्धांत एक जैसे हैं. CameraX, कैमरे के सामान्य फ़ंक्शन को इस्तेमाल के उदाहरणों में बदल देता है. इस वजह से, Camera1 में डेवलपर को जो काम करने पड़ते थे वे CameraX में अपने-आप हो जाते हैं. CameraX में चार UseCase होते हैं. इनका इस्तेमाल कैमरे से जुड़े कई कामों के लिए किया जा सकता है: Preview, ImageCapture, VideoCapture, और ImageAnalysis.
डेवलपर के लिए, CameraX के ज़रिए लो-लेवल की जानकारी मैनेज करने का एक उदाहरण यह है कि ViewPort को चालू UseCase के बीच शेयर किया जाता है. इससे यह पक्का होता है कि सभी UseCase को एक जैसे पिक्सल दिखें. Camera1 में, आपको यह जानकारी खुद मैनेज करनी होती है. अलग-अलग डिवाइसों के आसपेक्ट रेशियो अलग-अलग होते हैं. इसलिए, कैप्चर किए गए मीडिया से प्रीव्यू का मिलान करना मुश्किल होता है.
एक अन्य उदाहरण के तौर पर, CameraX, आपके दिए गए Lifecycle इंस्टेंस पर Lifecycle कॉलबैक को अपने-आप हैंडल करता है. इस आर्किटेक्चर की मदद से, CameraX आपके ऐप्लिकेशन के कैमरे से कनेक्शन को पूरे Android ऐक्टिविटी के लाइफ़साइकल के दौरान मैनेज करता है. इसमें ये मामले शामिल हैं: जब आपका ऐप्लिकेशन बैकग्राउंड में चला जाता है, तब कैमरे को बंद करना; जब स्क्रीन पर कैमरे की झलक दिखाने की ज़रूरत नहीं होती, तब उसे हटाना; और जब कोई दूसरी ऐक्टिविटी फ़ोरग्राउंड में आ जाती है, तब कैमरे की झलक को रोकना. जैसे, कोई वीडियो कॉल आना.
आखिर में, CameraX रोटेशन और स्केलिंग को मैनेज करता है. इसके लिए, आपको कोई अतिरिक्त कोड लिखने की ज़रूरत नहीं होती. अगर Activity की ओरिएंटेशन सेटिंग अनलॉक है, तो डिवाइस को घुमाने पर UseCase सेटअप हर बार किया जाता है. ऐसा इसलिए होता है, क्योंकि ओरिएंटेशन बदलने पर सिस्टम, Activity को मिटा देता है और फिर से बनाता है. इससे, हर बार डिसप्ले के ओरिएंटेशन से मैच करने के लिए, UseCases अपने टारगेट रोटेशन को डिफ़ॉल्ट रूप से सेट कर देता है. CameraX में रोटेशन के बारे में ज़्यादा पढ़ें.
ज़्यादा जानकारी देने से पहले, यहां CameraX के UseCase के बारे में खास जानकारी दी गई है. साथ ही, यह भी बताया गया है कि Camera1 ऐप्लिकेशन इससे कैसे जुड़ा होगा. (CameraX के कॉन्सेप्ट नीले रंग में और Camera1 के कॉन्सेप्ट हरे रंग में दिए गए हैं.)
CameraX |
|||
| CameraController / CameraProvider कॉन्फ़िगरेशन | |||
| ↓ | ↓ | ↓ | ↓ |
| झलक देखें | ImageCapture | VideoCapture | ImageAnalysis |
| ⁞ | ⁞ | ⁞ | ⁞ |
| झलक दिखाने वाली स्क्रीन को मैनेज करना और उसे कैमरे पर सेट करना | Camera पर PictureCallback सेट करें और takePicture() को कॉल करें | कैमरा और MediaRecorder के कॉन्फ़िगरेशन को किसी खास क्रम में मैनेज करना | प्रीव्यू सर्फ़ेस के आधार पर बनाया गया कस्टम विश्लेषण कोड |
| ↑ | ↑ | ↑ | ↑ |
| डिवाइस के हिसाब से कोड | |||
| ↑ | |||
| डिवाइस रोटेशन और स्केलिंग मैनेजमेंट | |||
| ↑ | |||
| कैमरा सेशन मैनेजमेंट (कैमरा चुनना, लाइफ़साइकल मैनेजमेंट) | |||
Camera1 |
|||
CameraX में परफ़ॉर्मेंस और साथ काम करने की सुविधा
CameraX, Android 5.0 (एपीआई लेवल 21) और इसके बाद के वर्शन पर काम करता है. यह मौजूदा Android डिवाइसों के 98% से ज़्यादा डिवाइसों के लिए उपलब्ध है. CameraX को इस तरह से बनाया गया है कि यह डिवाइसों के बीच के अंतर को अपने-आप मैनेज कर लेता है. इससे आपके ऐप्लिकेशन में, डिवाइस के हिसाब से कोड लिखने की ज़रूरत कम हो जाती है. इसके अलावा, हम CameraX Test Lab में, Android 5.0 से लेकर अब तक के सभी वर्शन पर 150 से ज़्यादा फ़िज़िकल डिवाइसों की जांच करते हैं. Test Lab में मौजूद डिवाइसों की पूरी सूची देखी जा सकती है.
CameraX, कैमरा स्टैक को चलाने के लिए Executor का इस्तेमाल करता है. अगर आपके ऐप्लिकेशन में थ्रेडिंग से जुड़ी कुछ खास ज़रूरी शर्तें हैं, तो CameraX पर अपना एक्ज़ीक्यूटर सेट किया जा सकता है.
अगर इसे सेट नहीं किया जाता है, तो CameraX, ऑप्टिमाइज़ किया गया डिफ़ॉल्ट इंटरनल Executor बनाता है और उसका इस्तेमाल करता है.
CameraX को बनाने के लिए इस्तेमाल किए गए कई प्लैटफ़ॉर्म एपीआई को, हार्डवेयर के साथ इंटरप्रोसेस कम्यूनिकेशन (आईपीसी) को ब्लॉक करने की ज़रूरत होती है. इसमें कभी-कभी सैकड़ों मिलीसेकंड लग सकते हैं. इस वजह से, CameraX सिर्फ़ बैकग्राउंड थ्रेड से इन एपीआई को कॉल करता है. इससे यह पक्का होता है कि मुख्य थ्रेड ब्लॉक न हो और यूज़र इंटरफ़ेस (यूआई) आसानी से काम करता रहे. थ्रेड के बारे में ज़्यादा पढ़ें.
अगर आपके ऐप्लिकेशन की टारगेट मार्केट में कम कॉन्फ़िगरेशन वाले डिवाइस शामिल हैं, तो CameraX, कैमरा लिमिटर की मदद से सेटअप करने में लगने वाला समय कम करने का तरीका उपलब्ध कराता है. हार्डवेयर कॉम्पोनेंट से कनेक्ट होने में कुछ समय लग सकता है. खास तौर पर, कम कॉन्फ़िगरेशन वाले डिवाइसों पर. इसलिए, उन कैमरों का सेट तय किया जा सकता है जिनकी आपके ऐप्लिकेशन को ज़रूरत है. CameraX, सेटअप के दौरान सिर्फ़ इन कैमरों से कनेक्ट होता है. उदाहरण के लिए, अगर ऐप्लिकेशन सिर्फ़ पीछे के कैमरे का इस्तेमाल करता है, तो वह DEFAULT_BACK_CAMERA की मदद से इस कॉन्फ़िगरेशन को सेट कर सकता है. इसके बाद, CameraX, सामने के कैमरे को शुरू नहीं करता है, ताकि लेटेन्सी कम हो सके.
Android डेवलपमेंट के कॉन्सेप्ट
इस गाइड में यह मानकर चला गया है कि आपको Android डेवलपमेंट के बारे में सामान्य जानकारी है. बुनियादी बातों के अलावा, यहां कुछ ऐसे कॉन्सेप्ट दिए गए हैं जिन्हें इस कोड को समझने से पहले जानना ज़रूरी है:
- व्यू बाइंडिंग, आपकी एक्सएमएल लेआउट फ़ाइलों के लिए एक बाइंडिंग क्लास जनरेट करती है. इससे आपको ऐक्टिविटी में अपने व्यू रेफ़रंस करने की सुविधा मिलती है. इसे बाद के कई कोड स्निपेट में दिखाया गया है. व्यू बाइंडिंग और
findViewById()(व्यू को रेफ़रंस करने का पिछला तरीका) के बीच कुछ अंतर हैंfindViewById(). हालांकि, यहां दिए गए कोड में, व्यू बाइंडिंग लाइनों कोfindViewById()के मिलते-जुलते कॉल से बदला जा सकता है. - एसिंक्रोनस कोरूटीन, एक साथ कई काम करने के लिए इस्तेमाल किया जाने वाला डिज़ाइन पैटर्न है. इसे Kotlin 1.3 में जोड़ा गया था. इसका इस्तेमाल, CameraX के उन तरीकों को हैंडल करने के लिए किया जा सकता है जो
ListenableFutureदिखाते हैं. Jetpack Concurrent लाइब्रेरी के वर्शन 1.1.0 से, इसे आसान बना दिया गया है. अपने ऐप्लिकेशन में एसिंक्रोनस कोरूटीन जोड़ने के लिए:- अपनी Gradle फ़ाइल में
implementation("androidx.concurrent:concurrent-futures-ktx:1.1.0")जोड़ें. - CameraX के ऐसे कोड को
launchब्लॉक या निलंबित फ़ंक्शन में रखें जोListenableFutureदिखाता है. - फ़ंक्शन कॉल में
await()कॉल जोड़ें, जोListenableFutureदिखाता है. - कोरूटीन के काम करने के तरीके के बारे में ज़्यादा जानने के लिए, कोरूटीन शुरू करना गाइड देखें.
- अपनी Gradle फ़ाइल में
सामान्य स्थितियों को माइग्रेट करना
इस सेक्शन में, Camera1 से CameraX पर सामान्य स्थितियों को माइग्रेट करने का तरीका बताया गया है.
हर उदाहरण में, Camera1, CameraX CameraProvider, और CameraX CameraController के इस्तेमाल के बारे में बताया गया है.
कोई कैमरा चुनना
कैमरा ऐप्लिकेशन में, सबसे पहले अलग-अलग कैमरे चुनने का विकल्प दिया जा सकता है.
Camera1
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 क्लास मैनेज करती है. CameraX, डिफ़ॉल्ट कैमरे का इस्तेमाल करने के सामान्य तरीके को आसान बनाता है. आपके पास यह तय करने का विकल्प होता है कि आपको डिफ़ॉल्ट रूप से सामने वाला कैमरा चाहिए या पीछे वाला कैमरा. इसके अलावा, CameraX के CameraControl ऑब्जेक्ट की मदद से, अपने ऐप्लिकेशन के लिए ज़ूम लेवल सेट किया जा सकता है. इसलिए, अगर आपका ऐप्लिकेशन ऐसे डिवाइस पर चल रहा है जो लॉजिकल कैमरे के साथ काम करता है, तो यह सही लेंस पर स्विच हो जाएगा.
यहां CameraController के साथ डिफ़ॉल्ट बैक कैमरे का इस्तेमाल करने के लिए, CameraX कोड दिया गया है:
// 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 preceding "Android development concepts" // section. 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()
झलक दिखाई जा रही है
ज़्यादातर कैमरा ऐप्लिकेशन को, किसी समय कैमरे का फ़ीड स्क्रीन पर दिखाना होता है. Camera1 के साथ, आपको लाइफ़साइकल कॉलबैक को सही तरीके से मैनेज करना होगा. साथ ही, आपको अपनी झलक के लिए रोटेशन और स्केलिंग भी तय करनी होगी.
इसके अलावा, Camera1 में आपको यह तय करना होगा कि आपको TextureView
या SurfaceView में से किसे प्रीव्यू के तौर पर इस्तेमाल करना है. दोनों विकल्पों में कुछ कमियां हैं. साथ ही, दोनों ही मामलों में Camera1 को रोटेशन और स्केलिंग को सही तरीके से मैनेज करना होता है. वहीं दूसरी ओर, CameraX के PreviewView में TextureView और SurfaceView, दोनों के लिए बुनियादी तौर पर लागू किए गए तरीके मौजूद हैं. CameraX यह तय करता है कि कौनसी सुविधा सबसे अच्छी है. यह फ़ैसला, डिवाइस के टाइप और आपके ऐप्लिकेशन के Android वर्शन जैसे फ़ैक्टर के आधार पर लिया जाता है. अगर दोनों में से कोई भी तरीका काम करता है, तो PreviewView.ImplementationMode का इस्तेमाल करके अपनी पसंद के तरीके के बारे में बताएं. COMPATIBLE विकल्प, झलक के लिए TextureView का इस्तेमाल करता है. साथ ही, PERFORMANCE वैल्यू, SurfaceView का इस्तेमाल करती है (जब मुमकिन हो).
Camera1
प्रीव्यू दिखाने के लिए, आपको 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
CameraX के CameraProvider के साथ, आपको PreviewView का इस्तेमाल करने की ज़रूरत नहीं है. हालांकि, यह Camera1 की तुलना में, अब भी प्रीव्यू सेटअप को काफ़ी आसान बना देता है. उदाहरण के तौर पर, इस उदाहरण में PreviewView का इस्तेमाल किया गया है. हालांकि, अगर आपकी ज़रूरतें ज़्यादा जटिल हैं, तो setSurfaceProvider() में पास करने के लिए, कस्टम SurfaceProvider लिखा जा सकता है.
यहां, CameraController की तरह Preview UseCase का मतलब नहीं है. इसलिए, आपको इसे सेट अप करना होगा:
// 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 preceding "Android development concepts" // section. 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() } }
टैप करके फ़ोकस करने की सुविधा
जब कैमरे से दिख रही झलक स्क्रीन पर होती है, तब एक सामान्य कंट्रोल यह होता है कि जब उपयोगकर्ता झलक पर टैप करे, तो फ़ोकस पॉइंट सेट हो जाए.
Camera1
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() की मदद से, टैप-टू-फ़ोकस की सुविधा चालू और बंद की जा सकती है. साथ ही, इससे जुड़े getter 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 का इस्तेमाल करते समय, यह तरीका अपनाएं:
- टैप इवेंट को हैंडल करने के लिए, जेस्चर डिटेक्टर सेट अप करें.
- टैप इवेंट के साथ,
MeteringPointFactory.createPoint()का इस्तेमाल करकेMeteringPointबनाएं. MeteringPointकी मदद से,FocusMeteringActionबनाएं.Cameraपर मौजूदCameraControlऑब्जेक्ट (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 // preceding "Android development concepts" section. 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-zoom scenario for scaleGestureDetector definition. var didConsume = scaleGestureDetector.onTouchEvent(event) if (!scaleGestureDetector.isInProgress) { didConsume = gestureDetector.onTouchEvent(event) } didConsume }
पिंच करके ज़ूम करने की सुविधा
कैमरे की झलक को ज़ूम इन और ज़ूम आउट करना, कैमरे की झलक में सीधे तौर पर बदलाव करने का एक और सामान्य तरीका है. डिवाइसों में कैमरों की संख्या बढ़ने की वजह से, उपयोगकर्ता यह भी उम्मीद करते हैं कि ज़ूम करने पर, सबसे अच्छी फ़ोकल लेंथ वाला लेंस अपने-आप चुना जाए.
Camera1
Camera1 का इस्तेमाल करके ज़ूम करने के दो तरीके हैं. 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, PreviewView के टच इवेंट को सुनता है, ताकि पिंच-टू-ज़ूम की सुविधा को अपने-आप मैनेज किया जा सके. setPinchToZoomEnabled() की मदद से, पिंच करके ज़ूम करने की सुविधा चालू और बंद की जा सकती है. साथ ही, इससे जुड़े गेटर isPinchToZoomEnabled() की मदद से, वैल्यू देखी जा सकती है.
getZoomState() वाला तरीका, LiveData ऑब्जेक्ट दिखाता है. इसका इस्तेमाल, CameraController पर ZoomState में हुए बदलावों को ट्रैक करने के लिए किया जाता है.
// 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 का इस्तेमाल करते समय, यह तरीका अपनाएं:
- पिंच इवेंट को हैंडल करने के लिए, स्केल जेस्चर डिटेक्टर सेट अप करें.
Camera.CameraInfoऑब्जेक्ट सेZoomStateपाएं.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-zoom scenario for gestureDetector definition. didConsume = gestureDetector.onTouchEvent(event) } didConsume }
फ़ोटो लेना
इस सेक्शन में, फ़ोटो कैप्चर करने की सुविधा को ट्रिगर करने का तरीका बताया गया है. इसमें यह भी बताया गया है कि आपको शटर बटन दबाने पर, टाइमर खत्म होने के बाद या अपनी पसंद के किसी अन्य इवेंट पर फ़ोटो कैप्चर करने की सुविधा को ट्रिगर करना है.
Camera1
Camera1 में, अनुरोध किए जाने पर फ़ोटो के डेटा को मैनेज करने के लिए, सबसे पहले Camera.PictureCallback को तय किया जाता है. यहां JPEG इमेज डेटा को मैनेज करने के लिए, PictureCallback का एक सामान्य उदाहरण दिया गया है:
// 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) } }
इसके बाद, जब भी आपको कोई फ़ोटो लेनी हो, तब अपने Camera इंस्टेंस पर takePicture() तरीके को कॉल करें. इस takePicture() तरीके में, अलग-अलग डेटा टाइप के लिए तीन अलग-अलग पैरामीटर होते हैं. पहला पैरामीटर ShutterCallback के लिए है. हालांकि, इस उदाहरण में इसे तय नहीं किया गया है. दूसरा पैरामीटर, PictureCallback के लिए है, ताकि कैमरे से मिले रॉ (बिना कंप्रेस किए गए) डेटा को मैनेज किया जा सके. तीसरा पैरामीटर, इस उदाहरण में इस्तेमाल किया गया पैरामीटर है. ऐसा इसलिए, क्योंकि यह JPEG इमेज डेटा को मैनेज करने के लिए PictureCallback है.
// Camera1: call takePicture on Camera instance, passing our PictureCallback. camera?.takePicture(null, null, picture)
CameraX: CameraController
CameraX का CameraController, इमेज कैप्चर करने के लिए Camera1 की तरह ही आसान है. इसके लिए, यह 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 फ़ंक्शन का पूरा उदाहरण देखने के लिए, इस सेक्शन में दिया गया 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 आपके लिए कई जटिल चीज़ों को हैंडल करके, आपका काम आसान बना देता है.
Camera1
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कॉन्फ़िगरेशन को हटाएं.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 के CameraController की मदद से, ImageCapture,
VideoCapture, और ImageAnalysis UseCase को अलग-अलग टॉगल किया जा सकता है. हालांकि, ऐसा तब तक किया जा सकता है, जब तक UseCases की सूची का इस्तेमाल एक साथ किया जा सकता है. 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 पैरामीटर उपलब्ध है. इसकी मदद से, वीडियो में ऑडियो रिकॉर्डिंग की सुविधा चालू या बंद की जा सकती है. ऑडियो रिकॉर्डिंग की सुविधा चालू करने के लिए, आपको यह पक्का करना होगा कि आपके पास माइक्रोफ़ोन ऐक्सेस करने की अनुमति हो. इसके अलावा, 1.3.0-alpha02 में stopRecording() तरीके को हटा दिया गया है. साथ ही, 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 पर, इसे तैयार करने के लिए कई तरीके हैं. MediaStore आउटपुट के विकल्प सेट करने के लिए, Call
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 } } } } }
अन्य संसाधन
हमारे पास Camera Samples GitHub रिपॉज़िटरी में, CameraX के कई ऐप्लिकेशन मौजूद हैं. इन सैंपल से पता चलता है कि इस गाइड में दिए गए उदाहरण, पूरी तरह से काम करने वाले Android ऐप्लिकेशन में कैसे फ़िट होते हैं.
अगर आपको CameraX पर माइग्रेट करने के लिए ज़्यादा मदद चाहिए या Android Camera API के सुइट के बारे में कोई सवाल पूछना है, तो कृपया CameraX Discussion Group पर हमसे संपर्क करें.