L'API Media Enhancement fournit une solution d'IA sur l'appareil à faible latence et protégeant la confidentialité, qui utilise l'accélération matérielle pour améliorer la qualité des contenus multimédias sans augmenter la taille de l'APK. Pour en savoir plus, consultez Comprendre les fonctionnalités d'amélioration des contenus multimédias.
Le mode Bitmap statique (EnhancementMode.BITMAP) est conçu pour le traitement d'images statiques décodées.
Le mode Bitmap nécessite que l'OS sérialise les données de pixels non compressées et les copie de la mémoire du processeur vers la mémoire du GPU via le bus système, puis renvoie le frame traité par une copie inverse. Il est optimisé pour l'exécution d'images uniques et n'est pas compatible avec le streaming vidéo en temps réel.
Ce workflow consiste à créer une session, à traiter un seul bitmap avec celle-ci, puis à gérer le résultat. Les exemples de code des sections précédentes couvrent ce cas d'utilisation en détail.
- Configurer les options : créez un objet
EnhancementOptionsen vous assurant queenhancementModeest défini surEnhancementMode.BITMAP. - Créer une session : utilisez le wrapper
createSessionAsyncque nous avons défini précédemment pour créer unEnhancementSession. Il s'agit d'un objet lourd. Ne le créez que lorsque cela est nécessaire. - Traiter l'image : appelez la session, votre bitmap d'entrée et les options souhaitées.
- Gérer le résultat : la fonction de suspension renvoie un nouveau Bitmap amélioré en cas de réussite ou génère une exception en cas d'échec.
- Libérer la session : il est essentiel d'appeler
session.release()lorsque vous avez terminé pour libérer les ressources GPU.
EnhancementSession est un objet de contexte lourd qui maintient un pipeline de mémoire GPU ou NPU persistant. Il alloue de la mémoire vive vidéo (VRAM) dédiée et des handles système natifs. Pour éviter les fuites de mémoire graves et les plantages potentiels de OutOfMemoryError, respectez les principes de cycle de vie suivants :
- Instanciation différée : ne créez pas de session tant que l'utilisateur n'a pas lancé d'action d'amélioration.
- Réutilisation stratégique : conservez et réutilisez une seule instance de session lorsque vous traitez plusieurs images avec des configurations identiques (dimensions et options activées).
- Analyse des requêtes : invoquez
session.release()immédiatement lorsque les tâches visuelles se terminent pour libérer les ressources matérielles partagées.
Initialiser le moteur d'amélioration
Cette méthode orchestre une vérification en deux étapes. Il vérifie si le matériel de l'appareil est compatible avec l'accélération, puis s'assure que les modules de machine learning requis sont présents.
L'exécution de cette étape en tant que prérequis permet d'éviter les échecs d'initialisation de l'exécution en validant les capacités avant que votre application ne tente de traiter des contenus multimédias.
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)
}
}
}
}
Créer des wrappers de processus de session et bitmap
Utilisez ces wrappers de coroutines Kotlin pour convertir les rappels de client basés sur des tâches en fonctions de suspension standards, ce qui permet une exécution plus propre et séquentielle.
// 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)
}
Exécuter le pipeline bitmap dans un ViewModel
Pour intégrer le pipeline d'amélioration dans l'architecture de votre application, utilisez un ViewModel pour gérer le cycle de vie de la session. Cette approche garantit que les ressources GPU importantes sont libérées lorsque ViewModel est effacé.
// 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()
}
}