बिटमैप मोड में मीडिया को बेहतर बनाने के लाइफ़साइकल के बारे में जानकारी

Media Enhancement API, डिवाइस पर काम करने वाला एआई सलूशन है. यह कम समय में काम करता है और निजता बनाए रखता है. यह हार्डवेयर ऐक्सलरेशन का इस्तेमाल करके, मीडिया की क्वालिटी को बेहतर बनाता है. साथ ही, इससे APK का साइज़ भी नहीं बढ़ता. ज़्यादा जानकारी के लिए, मीडिया को बेहतर बनाने की सुविधाओं के बारे में जानें लेख पढ़ें.

स्टैटिक बिटमैप मोड (EnhancementMode.BITMAP) को स्टैटिक और डिकोड की गई इमेज प्रोसेसिंग के लिए डिज़ाइन किया गया है.

बिटमैप मोड में काम करने के लिए, ओएस को बिना कंप्रेस किए गए पिक्सल डेटा को क्रम से लगाना होता है. साथ ही, इसे सीपीयू मेमोरी से सिस्टम बस के ज़रिए जीपीयू मेमोरी में कॉपी करना होता है. इसके बाद, प्रोसेस किए गए फ़्रेम को रिवर्स कॉपी के ज़रिए वापस भेजना होता है. इसे सिंगल-फ़्रेम के लिए ऑप्टिमाइज़ किया गया है. साथ ही, यह रीयल-टाइम वीडियो स्ट्रीमिंग के साथ काम नहीं करता.

इस वर्कफ़्लो में, एक सेशन बनाना, उसके साथ एक बिटमैप को प्रोसेस करना, और फिर नतीजे को मैनेज करना शामिल है. पिछले सेक्शन में दिए गए कोड के उदाहरणों में, इस इस्तेमाल के उदाहरण के बारे में ज़्यादा जानकारी दी गई है.

  1. विकल्प कॉन्फ़िगर करना: एक EnhancementOptions ऑब्जेक्ट बनाएं. साथ ही, यह पक्का करें कि enhancementMode को EnhancementMode.BITMAP पर सेट किया गया हो.
  2. सेशन बनाएं: EnhancementSession बनाने के लिए, हमने पहले जिस createSessionAsync रैपर को तय किया था उसका इस्तेमाल करें. यह एक हैवीवेट ऑब्जेक्ट है. इसलिए, इसे सिर्फ़ तब बनाएं, जब इसकी ज़रूरत हो.
  3. इमेज प्रोसेस करना: सेशन, इनपुट बिटमैप, और आपको जो विकल्प चाहिए उन्हें कॉल करें.
  4. नतीजे को मैनेज करना: suspend फ़ंक्शन, प्रोसेस पूरी होने पर एक नया और बेहतर बिटमैप दिखाता है. वहीं, प्रोसेस पूरी न होने पर एक अपवाद दिखाता है.
  5. सेशन रिलीज़ करें: जीपीयू संसाधनों को खाली करने के लिए, काम खत्म होने के बाद session.release() को कॉल करना ज़रूरी है.

EnhancementSession एक हैवीवेट कॉन्टेक्स्ट ऑब्जेक्ट है. यह लगातार जीपीयू या एनपीयू मेमोरी पाइपलाइन को बनाए रखता है. यह वीडियो रैम (वीआरएएम) और नेटिव सिस्टम हैंडल को अलग से असाइन करता है. मेमोरी लीक और संभावित OutOfMemoryError क्रैश से बचने के लिए, लाइफ़साइकल के इन सिद्धांतों का पालन करें:

  1. लेज़ी इंस्टैंटिएशन: जब तक उपयोगकर्ता कोई बेहतर कार्रवाई शुरू नहीं करता, तब तक सेशन न बनाएं.
  2. रणनीतिक तरीके से फिर से इस्तेमाल करना: एक जैसे कॉन्फ़िगरेशन (डाइमेंशन और टॉगल किए गए विकल्प) वाली कई इमेज को प्रोसेस करते समय, एक ही सेशन इंस्टेंस को बनाए रखें और उसका फिर से इस्तेमाल करें.
  3. प्रॉम्प्ट को बंद करना: विज़ुअल टास्क खत्म होने पर, शेयर किए गए हार्डवेयर संसाधनों को खाली करने के लिए, session.release() को तुरंत बंद करें.

बेहतर बनाने वाले इंजन को शुरू करना

इस तरीके से, दो चरणों में जांच की जाती है. यह पुष्टि करता है कि डिवाइस का हार्डवेयर, ऐक्सेलरेटर के साथ काम करता है या नहीं. इसके बाद, यह पक्का करता है कि ज़रूरी मशीन लर्निंग मॉड्यूल मौजूद हैं.

इस सुविधा को पहले से चालू करने पर, रनटाइम के दौरान शुरू होने में आने वाली समस्याओं को रोका जा सकता है. ऐसा इसलिए होता है, क्योंकि यह सुविधा आपके ऐप्लिकेशन के मीडिया को प्रोसेस करने से पहले, उसकी क्षमताओं की पुष्टि करती है.

class MediaSetupViewModel(application: Application) : AndroidViewModel(application) {
    private val enhancementClient = Enhancement.getClient(application)
    fun initializeEnhancementEngine() {
        viewModelScope.launch {
            try {
                // 1. Verify hardware capability
                val isSupported = enhancementClient.isDeviceSupportedAsync()
                if (!isSupported) {
                    notifyUiDeviceIncompatible()
                    return@launch
                }
                // 2. Verify and download the Google Play services ML modules
                val isInstalled = enhancementClient.isModuleInstalledAsync()
                if (!isInstalled) {
                    notifyUiDownloadingModels()
                    enhancementClient.installModule().await() 
                }
                notifyUiEngineReady()
            } catch (e: Exception) {

                // Handle potential errors during session creation or image processing.
                handleInitializationError(e)
            }
        }
    }
}

सेशन और बिटमैप प्रोसेस रैपर बनाता है

टास्क पर आधारित क्लाइंट कॉलबैक को स्टैंडर्ड सस्पेंडिंग फ़ंक्शन में बदलने के लिए, इन Kotlin कोरूटीन रैपर का इस्तेमाल करें. इससे, कोड को बेहतर तरीके से और क्रम से लागू किया जा सकेगा.

// Wraps the task-based createSession callback into a suspending function.
suspend fun EnhancementClient.createSessionAsync(
    options: EnhancementOptions,
    executor: Executor
): EnhancementSession = withContext(Dispatchers.Main) {
    suspendCancellableCoroutine { continuation ->

        // EnhancementSessionCallback handles session success or failure.
        val callback = object : EnhancementSessionCallback {
            override fun onSessionCreated(session: EnhancementSession) {
                continuation.resume(session)
            }
            override fun onSessionCreationFailed(status: Status) {
                continuation.resumeWithException(
                    Exception("Session creation failed: ${status.statusMessage} (${status.statusCode})")
                )
            }
            override fun onSessionDestroyed() {}
            override fun onSessionDisconnected(status: Status) {}
        }

        // Handles errors during the initial request trigger.
        this.createSession(options, callback).addOnFailureListener(executor) { e ->
            if (continuation.isActive) {
                continuation.resumeWithException(e)
            }
        }
    }
}

// Wraps this process in a suspending function for cleaner execution.
suspend fun EnhancementSession.processBitmapAsync(
    bitmap: Bitmap,
    options: EnhancementOptions
): Bitmap = suspendCancellableCoroutine { continuation ->

    // EnhancementCallback returns the processed bitmap or an error code.
    val callback = object : EnhancementCallback {
        override fun onBitmapProcessed(enhancedBitmap: Bitmap) {
            continuation.resume(enhancedBitmap)
        }
        override fun onError(statusCode: Int) {
            continuation.resumeWithException(
                Exception("Bitmap processing failed with status code: $statusCode")
            )
        }
        override fun onSurfaceProcessed(timestamp: Long) {}
    }
    this.process(bitmap, options, callback)
}

ViewModel में बिटमैप पाइपलाइन को एक्ज़ीक्यूट करना

अपने ऐप्लिकेशन आर्किटेक्चर में इमेज को बेहतर बनाने वाली पाइपलाइन को इंटिग्रेट करने के लिए, सेशन के लाइफ़साइकल को मैनेज करने के लिए ViewModel का इस्तेमाल करें. इस तरीके से यह पक्का किया जाता है कि ViewModel को हटाने पर, ज़्यादा जीपीयू रिसॉर्स रिलीज़ हो जाएं.

// Define a data class to hold image information.
data class ImageInfo(val bitmap: Bitmap)
// Define a UI state class to hold loading status, errors, and enhanced image.
data class EnhancementUiState(
    val isLoading: Boolean = false,
    val enhancementError: String? = null,
    val enhancedImage: ImageInfo? = null
)

class EnhancementViewModel(application: Application) : AndroidViewModel(application) {

    // Backing field for UI state, initialized with default values.
    private val _uiState = MutableStateFlow(EnhancementUiState())
    // Publicly exposed UI state flow for observation.
    val uiState: StateFlow<EnhancementUiState> = _uiState.asStateFlow()

// Initialize client to interact with the Media Enhancement service.
    private val enhancementClient: EnhancementClient = Enhancement.getClient(application)

// Single-thread executor for processing background enhancement tasks.
    private val enhancementExecutor = Executors.newSingleThreadExecutor()

// Track session state to enable reuse across multiple processing calls.
    private var enhancementSession: EnhancementSession? = null

// Primary function to trigger the enhancement workflow for a provided bitmap.
    fun enhanceImage(bitmap: Bitmap) {
        viewModelScope.launch(Dispatchers.IO) {
            _uiState.update { it.copy(isLoading = true, enhancementError = null) }
            try {
                // 1. Establish the session lazily on demand

// Define enhancement options (for example, enable upscale, tonemapping) based
// on bitmap dimensions.
                if (enhancementSession == null) {
                    val options = EnhancementOptions(
                        bitmap.width,
                        bitmap.height,
                        EnhancementMode.BITMAP,
                        enableTonemap = true,
                        enableDeblurDenoise = true,
                        enableDenoiseOnly = false,
                        enableUpscale = false,
                    )
                    enhancementSession = enhancementClient.createSessionAsync(options, enhancementExecutor)
                }
                val session = enhancementSession ?: throw IllegalStateException("Session unavailable.")
                // 2. Dispatch image through the neural pipeline
                val enhancedBitmap = session.processBitmapAsync(bitmap, session.defaultOptions)
                // 3. Render output to UI
                _uiState.update {
                    it.copy(enhancedImage = ImageInfo(bitmap = enhancedBitmap))
                }
            } catch (e: Exception) {
                _uiState.update { it.copy(enhancementError = e.message) }
            } finally {

// Ensure loading state is reset regardless of the outcome.
                _uiState.update { it.copy(isLoading = false) }
            }
        }
    }
    override fun onCleared() {
        // 4. Critical: Release native GPU hardware resources
        enhancementSession?.release()
        enhancementSession = null
        enhancementExecutor.shutdown()
        super.onCleared()
    }
}