La API de Media Enhancement proporciona una solución de IA integrado en el dispositivo con baja latencia y que preserva la privacidad que aprovecha la aceleración de hardware para ofrecer mejoras de alta calidad en los medios sin aumentar el tamaño del APK. Para obtener más información, consulta Información sobre las capacidades de mejora de medios.
El modo Bitmap estático (EnhancementMode.BITMAP) está diseñado para el procesamiento de imágenes estáticas decodificadas.
Para operar en el modo de mapa de bits, el SO debe serializar los datos de píxeles sin comprimir y copiarlos de la memoria de la CPU a través del bus del sistema a la memoria de la GPU, y devolver el fotograma procesado a través de una copia inversa. Está optimizado para la ejecución de un solo fotograma y no es compatible con la transmisión de video en tiempo real.
Este flujo de trabajo implica crear una sesión, procesar un solo mapa de bits con ella y, luego, controlar el resultado. Los ejemplos de código de las secciones anteriores abarcan este caso de uso en detalle.
- Configure options: Crea un objeto
EnhancementOptionsy asegúrate de queenhancementModeesté establecido enEnhancementMode.BITMAP. - Create session: Usa el wrapper
createSessionAsyncque definimos antes para crear unEnhancementSession. Este es un objeto pesado, por lo que solo debes crearlo cuando sea necesario. - Process image: Llama a la sesión, a tu mapa de bits de entrada y a las opciones que desees.
- Controla el resultado: La función de suspensión devuelve un mapa de bits nuevo y mejorado si se ejecuta correctamente o arroja una excepción si falla.
- Liberar sesión: Es fundamental que llames a
session.release()cuando termines para liberar los recursos de la GPU.
El objeto de contexto EnhancementSession es un objeto pesado que mantiene una canalización persistente de memoria de GPU o NPU. Asigna RAM de video (VRAM) dedicada y controladores del sistema nativos. Para evitar fugas de memoria graves y posibles fallas de OutOfMemoryError, sigue estos principios del ciclo de vida:
- Instanciación diferida: No crees una sesión hasta que el usuario inicie una acción de mejora.
- Reutilización estratégica: Mantén y reutiliza una sola instancia de sesión cuando proceses varias imágenes con configuraciones idénticas (dimensiones y opciones activadas).
- Desglose de la instrucción: Invoca
session.release()inmediatamente cuando finalicen las tareas visuales para liberar los recursos de hardware compartidos.
Inicializa el motor de mejora
Este método coordina una verificación en dos pasos. Verifica si el hardware del dispositivo admite la aceleración y, luego, se asegura de que estén presentes los módulos de aprendizaje automático requeridos.
Ejecutar este paso como requisito previo evita errores de inicialización en el tiempo de ejecución, ya que valida las capacidades antes de que tu app intente procesar contenido multimedia.
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)
}
}
}
}
Crea wrappers de procesos de sesión y mapa de bits
Usa estos wrappers de corrutinas de Kotlin para convertir las devoluciones de llamada del cliente basadas en tareas en funciones de suspensión estándar, lo que permite una ejecución más limpia y secuencial.
// 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)
}
Ejecuta la canalización de mapa de bits en un ViewModel
Para integrar la canalización de mejora en la arquitectura de tu aplicación, usa un ViewModel para administrar el ciclo de vida de la sesión. Este enfoque garantiza que los recursos de GPU pesados se liberen cuando se borra el 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()
}
}