1. Contenido multimedia: formatos, códecs y contenedores
Una aplicación multimedia integra imágenes, audio, vídeo y animaciones. Antes de programar hay que entender cómo se almacenan: los datos sin comprimir ocupan muchísimo (un minuto de vídeo Full HD sin comprimir son unos 10 GB), así que siempre se codifican con un códec y se guardan en un contenedor.
Códec y contenedor 🎬
| Tipo | Formato / códec | Compresión | Uso |
|---|---|---|---|
| Imagen | JPEG | Con pérdida | Fotografías |
| Imagen | PNG | Sin pérdida, transparencia | Iconos, capturas, gráficos |
| Imagen | WebP / AVIF | Ambas, más eficiente | Web y apps modernas |
| Imagen | SVG / Vector Drawable | Vectorial | Iconos escalables (en Android, VectorDrawable) |
| Audio | MP3, AAC | Con pérdida | Música, podcasts |
| Audio | Opus, Vorbis (OGG) | Con pérdida, libre | Voz, streaming, juegos |
| Audio | WAV, FLAC | Sin compresión / sin pérdida | Edición, efectos cortos |
| Vídeo | H.264 (AVC) | Con pérdida | El más compatible |
| Vídeo | H.265 (HEVC), VP9, AV1 | Con pérdida, más eficiente | 4K, streaming (AV1 es libre) |
| Contenedor | MP4, WebM, MKV, 3GP | — | Agrupan pistas de audio, vídeo y subtítulos |
- Resolución (píxeles), profundidad de color (bits por píxel), fotogramas por segundo, tasa de bits (kbps: calidad frente a tamaño) y, en audio, frecuencia de muestreo (44,1 kHz) y canales.
- Streaming adaptativo (HLS, MPEG-DASH): el vídeo se divide en trozos en varias calidades y el reproductor elige la que permite la conexión en cada momento.
- Al integrar contenido de terceros hay que respetar sus licencias (Creative Commons, royalty free) y los derechos de autor.
2. Imágenes y gráficos
Las imágenes consumen mucha memoria: una foto de 12 megapíxeles ocupa ~48 MB en RAM (4 bytes por píxel) aunque el JPEG pese 3 MB. Por eso se cargan reducidas al tamaño en que se muestran, de forma asíncrona y con caché. En Android se usan librerías como Coil (Compose) o Glide.
1// Imagen de los recursos (res/drawable) y de Internet con Coil
2@Composable
3fun FichaProducto(url: String) {
4 Column {
5 Image(painterResource(R.drawable.logo), contentDescription = "Logo de la tienda")
6 AsyncImage(
7 model = ImageRequest.Builder(LocalContext.current)
8 .data(url)
9 .crossfade(true)
10 .build(),
11 placeholder = painterResource(R.drawable.cargando),
12 error = painterResource(R.drawable.sin_imagen),
13 contentDescription = "Foto del producto",
14 contentScale = ContentScale.Crop,
15 modifier = Modifier.size(160.dp).clip(RoundedCornerShape(12.dp))
16 )
17 }
18}
19
20// Dibujar a mano en un lienzo (gráficos 2D)
21@Composable
22fun Grafico(valores: List<Float>) {
23 Canvas(Modifier.fillMaxWidth().height(160.dp)) {
24 val ancho = size.width / valores.size
25 valores.forEachIndexed { i, v ->
26 drawRect(Color(0xFF0E7490), topLeft = Offset(i * ancho + 4, size.height * (1 - v)),
27 size = Size(ancho - 8, size.height * v))
28 }
29 }
30}Animaciones
1var expandido by remember { mutableStateOf(false) }
2val altura by animateDpAsState(if (expandido) 240.dp else 80.dp, label = "altura")
3val rotacion by animateFloatAsState(if (expandido) 180f else 0f, label = "rotacion")
4
5Card(Modifier.height(altura).clickable { expandido = !expandido }) {
6 Icon(Icons.Default.ExpandMore, null, Modifier.rotate(rotacion))
7 AnimatedVisibility(expandido) { Text("Detalles del producto…") }
8}
9// Animaciones complejas diseñadas por diseñadores: Lottie (ficheros JSON exportados de After Effects)(sin mensajes)3. Reproducir y grabar audio
| Clase (Android) | Para qué |
|---|---|
ExoPlayer | (Jetpack Media3): la opción recomendada para música, podcasts y streaming (HLS, DASH), con listas de reproducción |
MediaPlayer | Reproductor clásico del sistema, sencillo para un único audio |
SoundPool | Efectos cortos cargados en memoria con muy poca latencia (juegos, pulsaciones) |
MediaRecorder | Grabar audio (y vídeo) del micrófono a un fichero |
AudioManager | Volumen y foco de audio: pausar la música cuando entra una llamada |
MediaSessionService | Reproducir en segundo plano con controles en la pantalla de bloqueo y en los auriculares |
1// build.gradle: implementation("androidx.media3:media3-exoplayer:1.4.1") y media3-ui
2@Composable
3fun ReproductorAudio(url: String) {
4 val contexto = LocalContext.current
5 val jugador = remember {
6 ExoPlayer.Builder(contexto).build().apply {
7 setMediaItem(MediaItem.fromUri(url))
8 prepare()
9 }
10 }
11 DisposableEffect(Unit) { onDispose { jugador.release() } } // liberar siempre los recursos
12
13 var reproduciendo by remember { mutableStateOf(false) }
14 IconButton(onClick = {
15 if (jugador.isPlaying) jugador.pause() else jugador.play()
16 reproduciendo = jugador.isPlaying
17 }) {
18 Icon(if (reproduciendo) Icons.Default.Pause else Icons.Default.PlayArrow, "Reproducir o pausar")
19 }
20}
21
22// Efectos de sonido con SoundPool
23val sonidos = SoundPool.Builder().setMaxStreams(4).build()
24val idSalto = sonidos.load(contexto, R.raw.salto, 1)
25sonidos.play(idSalto, 1f, 1f, 1, 0, 1f) // volumen izq, dcho, prioridad, bucles, velocidad
26
27// Grabar (requiere el permiso RECORD_AUDIO en tiempo de ejecución)
28val grabadora = MediaRecorder(contexto).apply {
29 setAudioSource(MediaRecorder.AudioSource.MIC)
30 setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
31 setAudioEncoder(MediaRecorder.AudioEncoder.AAC)
32 setOutputFile(File(contexto.filesDir, "nota.m4a"))
33 prepare(); start()
34}
35// … grabadora.stop(); grabadora.release()(sin mensajes)4. Vídeo y cámara
Para reproducir vídeo se usa también ExoPlayer con su vista PlayerView (controles, pantalla completa, subtítulos). Para capturar fotos y vídeo, CameraX simplifica la cámara del dispositivo; para casos sencillos basta con pedir una foto a la app de cámara del sistema.
1// 1) Foto con la app de cámara del sistema (sin permisos de cámara propios)
2val uri = FileProvider.getUriForFile(contexto, "${contexto.packageName}.fotos", File(contexto.cacheDir, "foto.jpg"))
3val hacerFoto = rememberLauncherForActivityResult(ActivityResultContracts.TakePicture()) { ok ->
4 if (ok) imagen = uri
5}
6Button(onClick = { hacerFoto.launch(uri) }) { Text("Hacer foto") }
7
8// 2) Elegir una imagen o vídeo de la galería (Photo Picker, sin permisos)
9val elegir = rememberLauncherForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri -> imagen = uri }
10Button(onClick = { elegir.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageAndVideo)) }) {
11 Text("Elegir de la galería")
12}
13
14// 3) Vista previa y captura dentro de la app con CameraX (requiere el permiso CAMERA)
15val controlador = remember { LifecycleCameraController(contexto) }
16AndroidView(factory = { PreviewView(it).apply { this.controller = controlador } })
17controlador.bindToLifecycle(lifecycleOwner)
18controlador.takePicture(opcionesSalida, ejecutor, callback)Permisos y privacidad
CAMERA), micrófono (RECORD_AUDIO) y fotos del usuario son permisos peligrosos: se piden en tiempo de ejecución, justo cuando hacen falta y explicando para qué. Siempre que se pueda se usa el selector de fotos o la app de cámara del sistema, que no necesitan permisos. Libera la cámara y los reproductores cuando la pantalla deja de verse para no gastar batería.Multimedia en la web
HTML5 incluye <video> y <audio> con una API de JavaScript (play(), pause(), currentTime, volume, muted y eventos play, pause, timeupdate, ended), <canvas> para dibujar y la Web Audio API para generar y procesar sonido. Con ellas se construyen reproductores propios, como en el reto siguiente.
El <video> no muestra los controles del navegador: hazlos tú. • #play alterna entre reproducir y pausar; su texto es «▶ Reproducir» cuando está pausado y «⏸ Pausa» cuando se reproduce (usa los eventos play y pause del vídeo, no el clic, para actualizar el texto). • #silencio alterna video.muted y su texto entre «🔇 Silenciar» y «🔊 Sonido». • El deslizador #volumen (0 a 100) fija video.volume (0 a 1) y escribe «Volumen: N %» en #nivel. • #adelantar avanza 10 segundos (sin pasar de la duración).
5. Buenas prácticas en aplicaciones multimedia
- Adapta los recursos a la densidad de pantalla (carpetas
drawable-mdpi…xxhdpio vectores) y al ancho de banda. - Carga en segundo plano y muestra un indicador; nunca bloquees el hilo principal.
- Caché de imágenes y descargas para no gastar datos del usuario.
- Libera reproductores, cámara y sensores en
onStop/onDispose. - Gestiona el foco de audio: pausa si otra app empieza a sonar o entra una llamada.
- Accesibilidad:
contentDescriptionen las imágenes, subtítulos en los vídeos, controles grandes. - Respeta los derechos del contenido y la privacidad de fotos y grabaciones.
6. Ejercicios
Elige formatos
Elige el formato más adecuado y justifícalo: 1. El icono de la app, que debe verse nítido en cualquier pantalla. 2. Las fotos de los productos de una tienda. 3. El efecto de «moneda» de un juego, que suena muchas veces por segundo. 4. La música de fondo de un juego (3 minutos). 5. Los vídeos de un curso que se ven en móviles con conexiones de todo tipo.
Grabadora de notas de voz
Diseña una app Android (Compose) para grabar notas de voz: botón para grabar/parar, lista de grabaciones con fecha y duración, reproducir cada una y borrarla. Indica los permisos, las clases que usarías para grabar y reproducir, dónde guardas los ficheros, cómo gestionas el ciclo de vida (qué pasa si el usuario sale de la app mientras graba) y cómo muestras la duración.