ध्यान दें: इस पेज पर Camera2 पैकेज के बारे में बताया गया है. अगर आपके ऐप्लिकेशन को Camera2 की खास और लो-लेवल सुविधाओं की ज़रूरत नहीं है, तो हमारा सुझाव है कि आप CameraX का इस्तेमाल करें. CameraX और Camera2, दोनों ही Android 5.0 (एपीआई लेवल 21) और इसके बाद के वर्शन पर काम करते हैं.
Android की सुविधा वाले किसी डिवाइस में कई कैमरे हो सकते हैं. हर कैमरा एक CameraDevice होता है और एक CameraDevice एक साथ कई स्ट्रीम आउटपुट कर सकता है.
ऐसा करने की एक वजह यह है कि CameraDevice से आने वाले एक स्ट्रीम, क्रम में चलने वाले कैमरा फ़्रेम को किसी खास टास्क के लिए ऑप्टिमाइज़ किया जाता है. जैसे, व्यूफ़ाइंडर दिखाना. वहीं, अन्य फ़्रेम का इस्तेमाल फ़ोटो लेने या वीडियो रिकॉर्ड करने के लिए किया जा सकता है. स्ट्रीम, पैरलल पाइपलाइन की तरह काम करती हैं. ये कैमरे से मिलने वाले रॉ फ़्रेम को एक-एक करके प्रोसेस करती हैं:
पैरलल प्रोसेसिंग से पता चलता है कि सीपीयू, जीपीयू या अन्य प्रोसेसर से मिलने वाली प्रोसेसिंग पावर के आधार पर, परफ़ॉर्मेंस की सीमाएं हो सकती हैं. अगर कोई पाइपलाइन, आने वाले फ़्रेम को प्रोसेस नहीं कर पाती है, तो वह उन्हें ड्रॉप करना शुरू कर देती है.
हर पाइपलाइन का अपना आउटपुट फ़ॉर्मैट होता है. आने वाला रॉ डेटा, हर पाइपलाइन से जुड़े इंप्लिसिट लॉजिक की मदद से, सही आउटपुट फ़ॉर्मैट में अपने-आप बदल जाता है. इस पेज के कोड सैंपल में इस्तेमाल किया गया CameraDevice, किसी खास कैमरे के लिए नहीं है. इसलिए, आगे बढ़ने से पहले, उपलब्ध सभी कैमरों की गिनती करें.
CameraDevice का इस्तेमाल करके, CameraCaptureSession बनाया जा सकता है. यह उस CameraDevice के लिए खास होता है. CameraDevice को CameraCaptureSession का इस्तेमाल करके, हर रॉ फ़्रेम के लिए फ़्रेम कॉन्फ़िगरेशन मिलना चाहिए. कॉन्फ़िगरेशन में कैमरे की विशेषताएं बताई जाती हैं. जैसे, ऑटोफ़ोकस, अपर्चर, इफ़ेक्ट, और एक्सपोज़र. हार्डवेयर की सीमाओं की वजह से, कैमरे के सेंसर में किसी भी समय सिर्फ़ एक कॉन्फ़िगरेशन चालू रहता है. इसे चालू कॉन्फ़िगरेशन कहा जाता है.
हालांकि, स्ट्रीम के इस्तेमाल के उदाहरण, CameraDevice का इस्तेमाल करने के पिछले तरीकों को बेहतर बनाते हैं और उन्हें आगे बढ़ाते हैं. इससे, कैप्चर सेशन को स्ट्रीम किया जा सकता है. इससे, अपने खास इस्तेमाल के उदाहरण के लिए, कैमरे से किसी और डिवाइस पर स्ट्रीम किए गए वीडियो को ऑप्टिमाइज़ किया जा सकता है. उदाहरण के लिए, वीडियो कॉल को ऑप्टिमाइज़ करने पर, बैटरी लाइफ़ बेहतर हो सकती है.
CameraCaptureSession, CameraDevice से जुड़ी सभी संभावित पाइपलाइन के बारे में बताता है. सेशन बनाने के बाद, पाइपलाइन जोड़ी या हटाई नहीं जा सकतीं.
CameraCaptureSession, CaptureRequest की सूची को मैनेज करता है. यह सूची, चालू कॉन्फ़िगरेशन बन जाती है.
CaptureRequest, सूची में एक कॉन्फ़िगरेशन जोड़ता है. साथ ही, CameraDevice से फ़्रेम पाने के लिए, उपलब्ध पाइपलाइन में से एक, एक से ज़्यादा या सभी पाइपलाइन चुनता है. कैप्चर सेशन के दौरान, कैप्चर करने के कई अनुरोध भेजे जा सकते हैं. हर अनुरोध, ऐक्टिव कॉन्फ़िगरेशन और रॉ इमेज पाने वाली आउटपुट पाइपलाइन के सेट को बदल सकता है.
बेहतर परफ़ॉर्मेंस के लिए, स्ट्रीम के इस्तेमाल के उदाहरणों का इस्तेमाल करना
स्ट्रीम के इस्तेमाल के उदाहरण, Camera2 कैप्चर सेशन की परफ़ॉर्मेंस को बेहतर बनाने का एक तरीका हैं. ये हार्डवेयर डिवाइस को पैरामीटर ट्यून करने के लिए ज़्यादा जानकारी देते हैं. इससे आपको अपने टास्क के लिए, कैमरे का बेहतर अनुभव मिलता है.
इससे कैमरा डिवाइस, हर स्ट्रीम के लिए उपयोगकर्ता के हिसाब से, कैमरा हार्डवेयर और सॉफ़्टवेयर पाइपलाइन को ऑप्टिमाइज़ कर पाता है. स्ट्रीम के इस्तेमाल के उदाहरणों के बारे में ज़्यादा जानने के लिए, setStreamUseCase देखें.
स्ट्रीम के इस्तेमाल के उदाहरणों की मदद से, यह बताया जा सकता है कि कैमरे से किसी और डिवाइस पर स्ट्रीम किए गए वीडियो का इस्तेमाल किस तरह किया जाता है. इसके अलावा, CameraDevice.createCaptureRequest() में टेंप्लेट भी सेट किया जा सकता है. इससे कैमरा हार्डवेयर, ट्यूनिंग, सेंसर मोड या कैमरा सेंसर की सेटिंग जैसे पैरामीटर को ऑप्टिमाइज़ कर पाता है. यह ऑप्टिमाइज़ेशन, क्वालिटी या इंतज़ार के समय के आधार पर किया जाता है. इससे, खास मामलों में बेहतर नतीजे मिलते हैं.
स्ट्रीम के इस्तेमाल के उदाहरणों में ये शामिल हैं:
DEFAULT: इसमें ऐप्लिकेशन के सभी मौजूदा व्यवहार शामिल हैं. यह स्ट्रीम के इस्तेमाल का कोई उदाहरण सेट न करने के बराबर है.PREVIEW: इसे व्यूफ़ाइंडर या ऐप्लिकेशन में इमेज का विश्लेषण करने के लिए इस्तेमाल करने का सुझाव दिया जाता है.STILL_CAPTURE: इसे अच्छी क्वालिटी और हाई रिज़ॉल्यूशन में कैप्चर करने के लिए ऑप्टिमाइज़ किया गया है. साथ ही, इससे झलक जैसे फ़्रेम रेट बनाए रखने की उम्मीद नहीं की जाती.VIDEO_RECORD: इसे अच्छी क्वालिटी का वीडियो कैप्चर करने के लिए ऑप्टिमाइज़ किया गया है. इसमें अच्छी क्वालिटी की इमेज स्टेबलाइज़ेशन की सुविधा भी शामिल है. हालांकि, इसके लिए ज़रूरी है कि डिवाइस में यह सुविधा काम करती हो और ऐप्लिकेशन में इसे चालू किया गया हो. इस विकल्प को चुनने पर, आउटपुट फ़्रेम रीयल टाइम से काफ़ी पीछे चल सकते हैं. ऐसा इसलिए होता है, ताकि वीडियो को बेहतर तरीके से स्टेबलाइज़ किया जा सके या अन्य प्रोसेसिंग की जा सके.VIDEO_CALL: इसे लंबे समय तक कैमरे का इस्तेमाल करने के लिए सुझाया जाता है, जहां बैटरी खत्म होने की चिंता होती है.PREVIEW_VIDEO_STILL: इसे सोशल मीडिया ऐप्लिकेशन या सिंगल स्ट्रीम के इस्तेमाल के मामलों के लिए सुझाया जाता है. यह एक मल्टीपर्पज़ स्ट्रीम है.VENDOR_START: इसका इस्तेमाल, ओईएम के तय किए गए इस्तेमाल के उदाहरणों के लिए किया जाता है.
CameraCaptureSession बनाना
कैमरा सेशन बनाने के लिए, उसे एक या उससे ज़्यादा आउटपुट बफ़र दें, जिनमें आपका ऐप्लिकेशन आउटपुट फ़्रेम लिख सके. हर बफ़र एक पाइपलाइन को दिखाता है. कैमरे का इस्तेमाल शुरू करने से पहले, आपको ऐसा करना होगा, ताकि फ़्रेमवर्क, डिवाइस की इंटरनल पाइपलाइन को कॉन्फ़िगर कर सके. साथ ही, ज़रूरी आउटपुट टारगेट पर फ़्रेम भेजने के लिए, मेमोरी बफ़र असाइन कर सके.
यहां दिए गए कोड स्निपेट से पता चलता है कि दो आउटपुट बफ़र के साथ कैमरा सेशन कैसे तैयार किया जा सकता है. इनमें से एक बफ़र SurfaceView और दूसरा ImageReader का होता है. previewSurface में PREVIEW स्ट्रीम के इस्तेमाल का उदाहरण और imReaderSurface में STILL_CAPTURE स्ट्रीम के इस्तेमाल का उदाहरण जोड़ने से, डिवाइस का हार्डवेयर इन स्ट्रीम को और भी बेहतर तरीके से ऑप्टिमाइज़ कर पाता है.
// Retrieve the target surfaces, which might be coming from a number of places: // 1. SurfaceView, if you want to display the image directly to the user // 2. ImageReader, if you want to read each frame or perform frame-by-frame // analysis // 3. OpenGL Texture or TextureView, although discouraged for maintainability // reasons // 4. RenderScript.Allocation, if you want to do parallel processing val surfaceView = findViewById<SurfaceView>( // ... ) val imageReader = ImageReader.newInstance( // ... ) // Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated(). val previewSurface = surfaceView.holder.surface val imReaderSurface = imageReader.surface val targets = listOf(previewSurface, imReaderSurface) // Create a capture session using the predefined targets; this also involves // defining the session state callback to be notified of when the session is // ready. // Setup Stream Use Case while setting up your Output Configuration. @RequiresApi(Build.VERSION_CODES.TIRAMISU) fun configureSession(device: CameraDevice, targets: List<Surface>) { val configs = mutableListOf<OutputConfiguration>() val streamUseCase = CameraMetadata .SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL targets.forEach { val config = OutputConfiguration(it) config.streamUseCase = streamUseCase.toLong() configs.add(config) } // ... device.createCaptureSession(session) }
फ़िलहाल, आपने कैमरे का ऐक्टिव कॉन्फ़िगरेशन तय नहीं किया है. सेशन कॉन्फ़िगर होने के बाद, कैप्चर करने के अनुरोध बनाए जा सकते हैं और उन्हें भेजा जा सकता है.
इनपुट को उनके बफ़र में लिखते समय, उन पर लागू किया गया ट्रांसफ़ॉर्मेशन, हर टारगेट के टाइप से तय होता है. यह Surface होना चाहिए. Android फ़्रेमवर्क को पता होता है कि ऐक्टिव कॉन्फ़िगरेशन में मौजूद रॉ इमेज को हर टारगेट के लिए सही फ़ॉर्मैट में कैसे बदला जाए. कन्वर्ज़न, पिक्सल फ़ॉर्मैट और खास Surface के साइज़ से कंट्रोल होता है.
फ़्रेमवर्क अपनी पूरी कोशिश करता है, लेकिन हो सकता है कि Surface कॉन्फ़िगरेशन के कुछ कॉम्बिनेशन काम न करें. इससे सेशन न बनने, अनुरोध भेजने पर रनटाइम से जुड़ी गड़बड़ी होने या परफ़ॉर्मेंस में गिरावट आने जैसी समस्याएं हो सकती हैं. यह फ़्रेमवर्क, डिवाइस, प्लैटफ़ॉर्म, और अनुरोध के पैरामीटर के कुछ खास कॉम्बिनेशन के लिए गारंटी देता है. createCaptureSession() के दस्तावेज़ में ज़्यादा जानकारी दी गई है.
Single CaptureRequests
हर फ़्रेम के लिए इस्तेमाल किए गए कॉन्फ़िगरेशन को CaptureRequest में एन्कोड किया जाता है. इसके बाद, इसे कैमरे पर भेजा जाता है. कैप्चर करने का अनुरोध बनाने के लिए, पहले से तय किए गए टेंप्लेट में से किसी एक का इस्तेमाल किया जा सकता है. इसके अलावा, पूरा कंट्रोल पाने के लिए TEMPLATE_MANUAL का इस्तेमाल किया जा सकता है. कोई टेंप्लेट चुनने पर, आपको अनुरोध के साथ इस्तेमाल करने के लिए एक या उससे ज़्यादा आउटपुट बफ़र देने होंगे. सिर्फ़ उन बफ़र का इस्तेमाल किया जा सकता है जो पहले से ही उस कैप्चर सेशन में तय किए गए हैं जिसका इस्तेमाल करना है.
कैप्चर करने के अनुरोध, बिल्डर पैटर्न का इस्तेमाल करते हैं. साथ ही, डेवलपर को कई अलग-अलग विकल्प सेट करने का मौका देते हैं. इनमें ऑटो-एक्सपोज़र, ऑटो-फ़ोकस, और लेंस अपर्चर शामिल हैं. कोई फ़ील्ड सेट करने से पहले, पक्का करें कि डिवाइस के लिए वह विकल्प उपलब्ध हो. इसके लिए, CameraCharacteristics.getAvailableCaptureRequestKeys() को कॉल करें. साथ ही, पक्का करें कि चुनी गई वैल्यू, कैमरे की सही विशेषता के साथ काम करती हो. जैसे, अपने-आप एक्सपोज़र मोड की सुविधा.
बिना किसी बदलाव के झलक देखने के लिए डिज़ाइन किए गए टेंप्लेट का इस्तेमाल करके, SurfaceView के लिए कैप्चर करने का अनुरोध बनाने के लिए, CameraDevice.TEMPLATE_PREVIEW का इस्तेमाल करें:
val session: CameraCaptureSession = /* ... */ // from CameraCaptureSession.StateCallback val captureRequest = session.device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW) captureRequest.addTarget(previewSurface)
कैप्चर करने का अनुरोध तय करने के बाद, अब इसे कैमरा सेशन में डिस्पैच किया जा सकता है:
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback val captureRequest: CaptureRequest.Builder = ... // from CameraDevice.createCaptureRequest() // The first null argument corresponds to the capture callback, which you // provide if you want to retrieve frame metadata or keep track of failed capture // requests that can indicate dropped frames; the second null argument // corresponds to the Handler used by the asynchronous callback, which falls // back to the current thread's looper if null. session.capture(captureRequest.build(), null, null)
जब किसी आउटपुट फ़्रेम को खास बफ़र में डाला जाता है, तो कैप्चर कॉलबैक ट्रिगर होता है. कई मामलों में, फ़्रेम को प्रोसेस करने पर, ImageReader.OnImageAvailableListener जैसे अतिरिक्त कॉलबैक ट्रिगर होते हैं. इस पॉइंट पर, तय किए गए बफ़र से इमेज डेटा को वापस पाया जा सकता है.
CaptureRequests को दोहराएं
सिंगल कैमरा के अनुरोधों को पूरा करना आसान होता है. हालांकि, लाइव झलक या वीडियो दिखाने के लिए, ये ज़्यादा काम के नहीं होते. ऐसे में, आपको सिर्फ़ एक फ़्रेम नहीं, बल्कि फ़्रेम की लगातार स्ट्रीम की ज़रूरत होगी. नीचे दिए गए कोड स्निपेट में, सेशन में बार-बार अनुरोध जोड़ने का तरीका बताया गया है:
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback val captureRequest: CaptureRequest.Builder = ... // from CameraDevice.createCaptureRequest() // This keeps sending the capture request as frequently as possible until // the session is torn down or session.stopRepeating() is called. session.setRepeatingRequest(captureRequest.build(), null, null)
बार-बार कैप्चर करने का अनुरोध करने पर, कैमरा डिवाइस लगातार इमेज कैप्चर करता है. इसके लिए, वह उपलब्ध कराए गए CaptureRequest में मौजूद सेटिंग का इस्तेमाल करता है. Camera2 API की मदद से, उपयोगकर्ता कैमरे से वीडियो भी कैप्चर कर सकते हैं. इसके लिए, उन्हें बार-बार CaptureRequests भेजना होगा. इस बारे में ज़्यादा जानने के लिए, GitHub पर मौजूद Camera2 सैंपल रिपॉज़िटरी देखें. यह स्लो मोशन वीडियो भी रेंडर कर सकता है. इसके लिए, यह बार-बार बर्स्ट CaptureRequests का इस्तेमाल करके, हाई-स्पीड (स्लो मोशन) वीडियो कैप्चर करता है. जैसा कि GitHub पर मौजूद Camera2 स्लो मोशन वीडियो सैंपल ऐप्लिकेशन में दिखाया गया है.
CaptureRequests को इंटरलीव करना
बार-बार कैप्चर करने का अनुरोध चालू होने पर, दूसरा कैप्चर अनुरोध भेजने के लिए, आपको मौजूदा अनुरोध को रोकने की ज़रूरत नहीं है. जैसे, व्यूफ़ाइंडर दिखाने और उपयोगकर्ताओं को फ़ोटो कैप्चर करने की सुविधा देने के लिए. इसके बजाय, आपको बार-बार कैप्चर करने का अनुरोध जारी रहने के दौरान, एक बार कैप्चर करने का अनुरोध जारी करना होगा.
इस्तेमाल किए जाने वाले किसी भी आउटपुट बफ़र को, कैमरा सेशन के हिस्से के तौर पर कॉन्फ़िगर करना ज़रूरी है. ऐसा तब करना ज़रूरी है, जब सेशन पहली बार बनाया गया हो. बार-बार किए जाने वाले अनुरोधों की प्राथमिकता, सिंगल-फ़्रेम या बर्स्ट अनुरोधों की तुलना में कम होती है. इससे, नीचे दिया गया सैंपल काम करता है:
val session: CameraCaptureSession = /* ... */ // from CameraCaptureSession.StateCallback // Create the repeating request and dispatch it. val repeatingRequest = session.device.createCaptureRequest( CameraDevice.TEMPLATE_PREVIEW ) repeatingRequest.addTarget(previewSurface) session.setRepeatingRequest(repeatingRequest.build(), null, null) // Some time later... // Create the single request and dispatch it. // NOTE: This can disrupt the ongoing repeating request momentarily. val singleRequest = session.device.createCaptureRequest( CameraDevice.TEMPLATE_STILL_CAPTURE ) singleRequest.addTarget(imReaderSurface) session.capture(singleRequest.build(), null, null)
हालांकि, इस तरीके में एक कमी है: आपको यह नहीं पता होता कि सिंगल अनुरोध कब होता है. नीचे दी गई इमेज में, अगर A बार-बार कैप्चर करने का अनुरोध है और B सिंगल-फ़्रेम कैप्चर करने का अनुरोध है, तो सेशन, अनुरोध की सूची को इस तरह प्रोसेस करता है:
अनुरोध B के चालू होने से पहले, A से बार-बार किए जाने वाले आखिरी अनुरोध और अगली बार A का फिर से इस्तेमाल किए जाने के बीच की इंतज़ार की अवधि की कोई गारंटी नहीं है. इसलिए, आपको कुछ फ़्रेम छूटे हुए दिख सकते हैं. इस समस्या को कम करने के लिए, कुछ चीज़ें की जा सकती हैं:
अनुरोध A से अनुरोध B में आउटपुट टारगेट जोड़ें. इस तरह, जब B का फ़्रेम तैयार हो जाता है, तो उसे A के आउटपुट टारगेट में कॉपी कर दिया जाता है. उदाहरण के लिए, वीडियो स्नैपशॉट लेते समय फ़्रेम रेट को स्थिर बनाए रखने के लिए, यह ज़रूरी है. ऊपर दिए गए कोड में, अनुरोध बनाने से पहले
singleRequest.addTarget(previewSurface)को जोड़ा जाता है.इस खास स्थिति के लिए डिज़ाइन किए गए टेंप्लेट के कॉम्बिनेशन का इस्तेमाल करें. जैसे, ज़ीरो-शटर-लैग.