Genera miniaturas de contenido multimedia

Las miniaturas de medios brindan a los usuarios una vista previa rápida de imágenes y videos, lo que permite una navegación más rápida y hace que la interfaz de la aplicación sea más visual. atractivos y atractivos. Debido a que las miniaturas son más pequeñas que el contenido multimedia de tamaño completo, ayudan a ahorrar memoria, espacio de almacenamiento y ancho de banda, a la vez que mejoran el rendimiento el rendimiento de navegación.

Según el tipo de archivo y el acceso a los archivos que tienes en tu aplicación y tus recursos multimedia, puedes crear miniaturas de diferentes maneras.

Crea una miniatura con una biblioteca de carga de imágenes

Las bibliotecas de carga de imágenes hacen gran parte del trabajo pesado por ti. pueden manejar el almacenamiento en caché junto con la lógica para recuperar los medios de origen desde la red local recurso basado en un Uri. El siguiente código demuestra el uso de la La biblioteca de carga de imágenes de Coil funciona tanto para imágenes como para videos. y funciona en un recurso local o de red.

// Use Coil to create and display a thumbnail of a video or image with a specific height
// ImageLoader has its own memory and storage cache, and this one is configured to also
// load frames from videos
val videoEnabledLoader = ImageLoader.Builder(context)
    .components {
        add(VideoFrameDecoder.Factory())
    }.build()
// Coil requests images that match the size of the AsyncImage composable, but this allows
// for precise control of the height
val request = ImageRequest.Builder(context)
    .data(mediaUri)
    .size(Int.MAX_VALUE, THUMBNAIL_HEIGHT)
    .build()
AsyncImage(
    model = request,
    imageLoader = videoEnabledLoader,
    modifier = Modifier
        .clip(RoundedCornerShape(20))    ,
    contentDescription = null
)

Si es posible, crea miniaturas del servidor. Consulta Cómo cargar imágenes. para obtener detalles sobre cómo cargar imágenes con Compose y Cómo cargar mapas de bits grandes de forma eficiente para obtener orientación sobre cómo trabajar con imágenes grandes.

Crea una miniatura a partir de un archivo de imagen local

Para obtener las imágenes de las miniaturas, se reduce la escala de forma eficiente y se conservan calidad, evitar el uso excesivo de memoria, lidiar con una variedad de imágenes formatos y hacer un uso correcto de los datos Exif.

El método createImageThumbnail hace todo esto, siempre que tengas acceso a la ruta de acceso del archivo de imagen.

val bitmap = ThumbnailUtils.createImageThumbnail(File(file_path), Size(640, 480), null)

Si solo tienes el Uri, puedes usar el método loadThumbnail en ContentResolver a partir de Android 10 (nivel de API 29).

val thumbnail: Bitmap =
        applicationContext.contentResolver.loadThumbnail(
        content-uri, Size(640, 480), null)

ImageDecoder, disponible a partir de Android 9, nivel de API 28, tiene algunas opciones sólidas para volver a muestrear la imagen a medida que la decodificas para evitar memoria adicional usar.

class DecodeResampler(val size: Size, val signal: CancellationSignal?) : OnHeaderDecodedListener {
    private val size: Size

   override fun onHeaderDecoded(decoder: ImageDecoder, info: ImageInfo, source:
       // sample down if needed.
        val widthSample = info.size.width / size.width
        val heightSample = info.size.height / size.height
        val sample = min(widthSample, heightSample)
        if (sample > 1) {
            decoder.setTargetSampleSize(sample)
        }
    }
}

val resampler = DecoderResampler(size, null)
val source = ImageDecoder.createSource(context.contentResolver, imageUri)
val bitmap = ImageDecoder.decodeBitmap(source, resampler);

Puedes usar BitmapFactory para crear miniaturas para apps que se orienten a versiones anteriores de Android. BitmapFactory.Options tiene una configuración para decodificar solo el límites de una imagen para la reproducción de muestras.

Primero, decodifica solo los límites del mapa de bits en BitmapFactory.Options:

private fun decodeResizedBitmap(context: Context, uri: Uri, size: Size): Bitmap?{
    val boundsStream = context.contentResolver.openInputStream(uri)
    val options = BitmapFactory.Options()
    options.inJustDecodeBounds = true
    BitmapFactory.decodeStream(boundsStream, null, options)
    boundsStream?.close()

Usa width y height de BitmapFactory.Options para configurar la muestra. tamaño:

if ( options.outHeight != 0 ) {
        // we've got bounds
        val widthSample = options.outWidth / size.width
        val heightSample = options.outHeight / size.height
        val sample = min(widthSample, heightSample)
        if (sample > 1) {
            options.inSampleSize = sample
        }
    }

Decodifica la transmisión. El tamaño de la imagen resultante se muestrea por potencias de dos según inSampleSize.

    options.inJustDecodeBounds = false
    val decodeStream = context.contentResolver.openInputStream(uri)
    val bitmap =  BitmapFactory.decodeStream(decodeStream, null, options)
    decodeStream?.close()
    return bitmap
}

Crea una miniatura a partir de un archivo de video local

Obtener las imágenes de las miniaturas de los videos implica muchos de los mismos desafíos que con obtener miniaturas de imágenes, pero el tamaño de los archivos puede ser mucho más grande y obtener una video representativo no siempre es tan sencillo como elegir el primer fotograma.

El método createVideoThumbnail es una buena opción si tienes acceso a la ruta del archivo de video.

val bitmap = ThumbnailUtils.createVideoThumbnail(File(file_path), Size(640, 480), null)

Si solo tienes acceso a un URI de contenido, puedes usar MediaMetadataRetriever.

Primero, comprueba si el video tiene una miniatura incorporada y úsala si posible:

private suspend fun getVideoThumbnailFromMediaMetadataRetriever(context: Context, uri: Uri, size: Size): Bitmap? {
    val mediaMetadataRetriever = MediaMetadataRetriever()
    mediaMetadataRetriever.setDataSource(context, uri)
    val thumbnailBytes = mediaMetadataRetriever.embeddedPicture
    val resizer = Resizer(size, null)
    ImageDecoder.createSource(context.contentResolver, uri)
    // use a built-in thumbnail if the media file has it
    thumbnailBytes?.let {
        return ImageDecoder.decodeBitmap(ImageDecoder.createSource(it));
    }

Recupera el ancho y la altura del video de MediaMetadataRetriever a calcular el factor de escala:

val width = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH)
            ?.toFloat() ?: size.width.toFloat()
    val height = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT)
            ?.toFloat() ?: size.height.toFloat()
    val widthRatio = size.width.toFloat() / width
    val heightRatio = size.height.toFloat() / height
    val ratio = max(widthRatio, heightRatio)

En Android 9 y versiones posteriores (nivel de API 28), MediaMetadataRetriever puede mostrar un ajuste marco:

if (ratio > 1) {
        val requestedWidth = width * ratio
        val requestedHeight = height * ratio
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            val frame = mediaMetadataRetriever.getScaledFrameAtTime(
                -1, OPTION_PREVIOUS_SYNC,
                requestedWidth.toInt(), requestedHeight.toInt())
            mediaMetadataRetriever.close()
            return frame
        }
    }

De lo contrario, muestra el primer fotograma sin escalar:

    // consider scaling this after the fact
    val frame = mediaMetadataRetriever.frameAtTime
    mediaMetadataRetriever.close()
    return frame
}