ExoPlayer es una biblioteca de reproducción multimedia de código abierto para Android desarrollada por Google como alternativa al MediaPlayer integrado. ExoPlayer proporciona reproducción de audio y video en varios formatos, incluyendo DASH, HLS, SmoothStreaming y archivos multimedia comunes, a través de una API única con soporte para streaming adaptativo. Según Google I/O (2024), ExoPlayer se utiliza en más de 60 000 aplicaciones Android, incluyendo YouTube, Google TV y Netflix.
Puntos clave
ExoPlayer es una biblioteca de reproducción multimedia de código abierto (Apache 2.0) para Android, cuya primera versión fue lanzada por Google en 2014. A diferencia del MediaPlayer integrado, que es una capa delgada sobre el códec multimedia del sistema (MediaCodec), ExoPlayer está implementado completamente a nivel de Java/Kotlin, lo que brinda a los desarrolladores control total sobre cada etapa de la reproducción.
Arquitectónicamente, ExoPlayer está construido sobre un principio modular: cada aspecto de la reproducción se implementa mediante un componente separado. El módulo principal (exoplayer-core) contiene el reproductor, los renderizadores, los selectores de pistas y la gestión del búfer. Los módulos adicionales (exoplayer-dash, exoplayer-hls, exoplayer-rtsp, exoplayer-smoothstreaming) añaden soporte para los protocolos de streaming correspondientes. Esta arquitectura permite a las aplicaciones incluir solo los componentes necesarios, minimizando el tamaño del APK.
ExoPlayer 2 (la versión principal actual) fue lanzado en 2018 y trajo una revisión radical de la API: introducción de la interfaz Player, soporte para Compose mediante AndroidX Media3, manejo mejorado de errores y algoritmos adaptativos de selección de pistas. A partir de la versión 2.19 (2024), ExoPlayer pasó a formar parte de la biblioteca AndroidX Media, simplificando la integración y las actualizaciones a través del gestor de dependencias estándar de Android.
En el núcleo de ExoPlayer se encuentra el componente ExoPlayerImpl, que gestiona el ciclo de vida de la reproducción. Contiene MediaSource (fuente de medios), TrackSelector (selección de pistas), LoadControl (gestión de almacenamiento en búfer) y una lista de Renderer (renderizadores para video, audio, subtítulos y metadatos). Cada componente puede reemplazarse con una implementación propia mediante Builder o métodos de fábrica.
Para la renderización de video, ExoPlayer utiliza SurfaceView, TextureView o SphericalSurfaceView (para video de 360 grados). La opción recomendada es SurfaceView, ya que proporciona aceleración por hardware y un consumo mínimo de energía. Para Jetpack Compose, la biblioteca AndroidX Media3 proporciona el componente `AndroidView`, que envuelve SurfaceView, y el experimental `Media3Compose` con integración nativa de Compose.
ExoPlayer ofrece una amplia gama de funciones más allá de un reproductor multimedia común. Las capacidades clave incluyen soporte para todos los protocolos de streaming populares, un sistema DRM flexible, conmutación adaptativa de tasa de bits (ABR) y herramientas de análisis de reproducción.
| Función | Descripción | Módulo |
|---|---|---|
| DASH | Streaming adaptativo según estándar MPEG | exoplayer-dash |
| HLS | HTTP Live Streaming de Apple | exoplayer-hls |
| SmoothStreaming | Streaming adaptativo de Microsoft | exoplayer-smoothstreaming |
| RTSP | Protocolo de streaming en tiempo real para live | exoplayer-rtsp |
| Widevine | Protección de contenido DRM | exoplayer-drm |
| Concatenation | Combinación de varios archivos multimedia | exoplayer-core |
| Ads | Integración IMA SDK para publicidad | exoplayer-ima |
ABR (Adaptive Bitrate) en ExoPlayer se implementa a través de la interfaz AdaptiveTrackSelection. Por defecto se utiliza AdaptiveTrackSelectionFactory, que crea DefaultAdaptiveTrackSelection — un algoritmo equilibrado que tiene en cuenta el ancho de banda de la red y el tamaño del búfer. Para escenarios específicos, se pueden conectar implementaciones alternativas: BOLA (minimización de cambios), RandomAdaptiveTrackSelection (pruebas) o una implementación propia a través de la interfaz TrackSelection.
BandwidthMeter — otro componente clave responsable de medir el ancho de banda de la red. Por defecto se utiliza DefaultBandwidthMeter, que recopila estadísticas de todas las solicitudes HTTP del reproductor. El desarrollador puede extenderlo para tener en cuenta factores adicionales: tipo de red (Wi-Fi, 4G, 5G), señal de torre celular o costo de datos para el usuario.
ExoPlayer admite Widevine (L1, L3), PlayReady y ClearKey DRM. La obtención de licencias se configura a través de DrmSessionManager, que interactúa con el servidor de licencias. Para video premium (4K HDR), se requiere soporte de hardware Widevine L1, que proporciona protección a nivel de chip y evita la captura de pantalla. La elección del sistema DRM depende de la plataforma objetivo: Widevine se usa en Android y Chromecast, PlayReady en Xbox y Windows, FairPlay en iOS y Apple TV. ExoPlayer determina automáticamente el sistema DRM disponible basándose en el archivo MPD y no requiere configuración manual para escenarios básicos.
La conexión de ExoPlayer a un proyecto Android se realiza mediante dependencias de Gradle. A partir de la versión 2.19, ExoPlayer forma parte de AndroidX Media (androidx.media3), lo que garantiza compatibilidad con Jetpack Compose y otros componentes de AndroidX. Veamos una integración paso a paso en Kotlin.
Agregue los módulos de ExoPlayer al archivo build.gradle (Module). El conjunto mínimo incluye core, ui y el módulo para el protocolo de streaming necesario. Para la reproducción de contenido DASH se requiere exoplayer-dash, para HLS — exoplayer-hls.
dependencies {
implementation "androidx.media3:media3-exoplayer:1.5.0"
implementation "androidx.media3:media3-ui:1.5.0"
implementation "androidx.media3:media3-exoplayer-dash:1.5.0"
implementation "androidx.media3:media3-exoplayer-hls:1.5.0"
}
Creación y configuración del reproductor en una Activity o Fragment. ExoPlayer utiliza PlayerView (o StyledPlayerView para personalización) para mostrar video y controles integrados. La configuración mínima incluye crear el reproductor mediante ExoPlayer.Builder y pasar un MediaItem con la URI de origen.
class PlayerActivity : AppCompatActivity() {
private var player: ExoPlayer? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_player)
player = ExoPlayer.Builder(this).build()
val playerView = findViewById<StyledPlayerView>(R.id.playerView)
playerView.player = player
val mediaItem = MediaItem
.Builder()
.setUri("https://example.com/video.mp4")
.build()
player?.setMediaItem(mediaItem)
player?.prepare()
}
override fun onDestroy() {
player?.release()
super.onDestroy()
}
}
Para aplicaciones de producción, es necesario el manejo de posibles errores de reproducción: fallos de red, formatos no compatibles, errores de licencia DRM. ExoPlayer proporciona la interfaz Player.Listener con los métodos onPlayerError, onPlaybackStateChanged y onPlayWhenReadyChanged. Se recomienda mostrar al usuario mensajes de error informativos con opción de reintento.
Elegir entre ExoPlayer y el MediaPlayer integrado es una pregunta común al desarrollar aplicaciones Android. MediaPlayer ha sido parte del SDK de Android desde la versión 1.0, mientras que ExoPlayer es una biblioteca que requiere dependencia de Gradle. Veamos sus diferencias según criterios clave.
| Criterio | ExoPlayer | MediaPlayer |
|---|---|---|
| Arquitectura | Modular, completamente en Java/Kotlin | Capa sobre componentes C++ del sistema |
| Streaming | DASH, HLS, SmoothStreaming, RTSP | Solo archivos locales y HLS (Android 9+) |
| Personalización | Total (cualquier componente reemplazable) | Mínima (solo parámetros) |
| Actualizaciones | Mediante Google Play o APK (independiente del SO) | Solo mediante actualización de Android (OTA) |
| Tamaño APK | +1–3 MB (según los módulos) | 0 (parte del SO) |
| Min SDK | API 16+ (Android 4.1) | API 1+ |
| DRM | Widevine, PlayReady, ClearKey | Widevine (limitado) |
ExoPlayer gana en escenarios que requieren streaming (DASH, HLS), personalización de interfaz, soporte DRM e independencia de la versión de Android. MediaPlayer es adecuado para aplicaciones simples que reproducen archivos multimedia locales donde el tamaño del APK es crítico y no hay requisitos de streaming adaptativo. Para aplicaciones modernas con contenido de red, ExoPlayer es la opción clara.
Excepción — aplicaciones con SDK mínimo inferior a API 16 (Android 4.1), que prácticamente no existen en el mercado. Para todas las aplicaciones con targetSdk 33+ y SDK mínimo 21+, ExoPlayer no solo es preferible, sino necesario para admitir formatos modernos como AV1 y Dolby Vision mediante las extensiones correspondientes.
Para aplicaciones de producción, la integración básica de ExoPlayer a menudo requiere configuración adicional: gestión de caché, optimización del búfer para redes móviles, integración con análisis y personalización de la selección de pistas de audio. Veamos escenarios de uso avanzados.
CacheDataSourceFactory permite almacenar en caché los segmentos descargados en el almacenamiento local, ahorrando tráfico en visualizaciones repetidas y permitiendo la reproducción sin conexión. ExoPlayer utiliza SimpleCache de la biblioteca exoplayer-cas — almacenamiento en caché basado en archivos con soporte de evacuación LRU y límites de tamaño.
val cache = SimpleCache(
cacheDir,
LeastRecentlyUsedCacheEvictor(50 * 1024 * 1024),
AppDatabaseProvider(this)
)
val cacheDataSourceFactory = CacheDataSource
.Factory()
.setCache(cache)
val player = ExoPlayer.Builder(this)
.setMediaSourceFactory(
DefaultMediaSourceFactory(cacheDataSourceFactory)
)
.build()
En aplicaciones móviles, el manejo de la conmutación entre Wi-Fi y red celular es crítico. ExoPlayer proporciona ConnectivityManager para rastrear el estado de la red. Ante una pérdida de conexión, se recomienda no detener el reproductor sino cambiarlo al estado PAUSED conservando la posición. Después de restaurar la conexión, reanude la reproducción recargando los segmentos.
Con el lanzamiento de AndroidX Media3, la biblioteca obtuvo soporte experimental para Jetpack Compose. El componente `AndroidView` envuelve SurfaceView o PlayerView, garantizando compatibilidad con la jerarquía de Compose. Para una integración más profunda, se utiliza la biblioteca `androidx.media3:media3-ui-compose`, que proporciona controles de reproductor compatibles con Compose.
@Composable
fun VideoPlayer(uri: String) {
val context = LocalContext.current
val player = remember {
ExoPlayer.Builder(context).build()
.also { it.setMediaItem(MediaItem.fromUri(uri)) }
}
DisposableEffect(key = null) {
player.prepare()
onDispose { player.release() }
}
AndroidView(
factory = { StyledPlayerView(context).also { it.player = player } }
)
}
Preguntas frecuentes
ExoPlayer admite Android API 16+ (Android 4.1 Jelly Bean). Sin embargo, para DASH y HLS con segmentos fMP4 se recomienda API 21+ (Android 5.0 Lollipop). Los módulos DRM (Widevine) requieren API 19+, y la decodificación HEVC por hardware está disponible desde API 21.
Sí, ExoPlayer es excelente para audio. Para reproducción solo de audio no se requiere SurfaceView ni PlayerView. ExoPlayer seleccionará automáticamente el renderizador de audio y funcionará en segundo plano. Para reproducción en segundo plano, se recomienda usar MediaSessionService para la integración con los controles del sistema.
El TrackSelector en ExoPlayer gestiona la selección de pistas de audio. Para cambiar, use player.getCurrentTracks().groups y player.setTrackSelectionParameters() especificando el idioma de audio preferido. Para streams DASH y HLS con múltiples pistas de audio, el cambio se produce sin interrupción de la reproducción.
Sí, ExoPlayer admite subtítulos incrustados (en contenedores MP4, WebM) y externos en formatos TTML, SRT, VTT y CEA-608. Para subtítulos externos se utiliza SingleSampleMediaSource o MergingMediaSource para fusionar con el flujo de video. La visualización de subtítulos estilizados se configura mediante CaptionStyleCompat.
Para streaming de baja latencia, configure LoadControl con valores de búfer objetivo más pequeños: use DefaultLoadControl.Builder().setTargetBufferBytes(1024 * 512).setBufferDurationsMs(500, 2000, 500, 1000) para HLS LL y DASH LL. También establezca minPlaybackSpeed = 1.02 para un llenado acelerado del búfer cuando se retrase respecto a la transmisión en vivo.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también