简介:本文深入探讨如何使用 Media3 中的 ExoPlayer 库构建高性能音视频播放器,涵盖网络优化、自定义UI、多格式支持及错误处理等关键技术点。
在《Media3 - ExoPlayer 打造音视频播放器(上篇)》中,我们介绍了 ExoPlayer 的基本架构、初始化及简单播放流程。本篇将深入探讨如何利用 ExoPlayer 的高级特性,打造一个功能丰富、性能卓越的音视频播放器,包括网络优化、自定义UI、多格式支持及错误处理等关键技术点。
ExoPlayer 提供了强大的缓存机制,通过 CacheDataSource 可以有效减少重复下载,提升播放流畅度。
// 创建缓存目录File cacheDir = new File(context.getCacheDir(), "exo_cache");// 创建缓存实例LeastRecentlyUsedCacheEvictor evictor = new LeastRecentlyUsedCacheEvictor(100 * 1024 * 1024); // 100MBSimpleCache cache = new SimpleCache(cacheDir, evictor);// 创建带缓存的DataSourceDataSource.Factory cacheDataSourceFactory = new CacheDataSource.Factory().setCache(cache).setUpstreamDataSourceFactory(upstreamDataSourceFactory).setCacheWriteDataSinkFactory(null) // 可选:禁用写入缓存.setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR);
关键点:
LeastRecentlyUsedCacheEvictor 实现了LRU策略,确保最近使用的数据被保留。FLAG_IGNORE_CACHE_ON_ERROR 确保缓存读取失败时不会阻塞播放。对于长视频或直播流,预加载和分段加载可以显著提升用户体验。
// 启用预加载LoadControl loadControl = new DefaultLoadControl.Builder().setBufferDurationsMs(minBufferMs, maxBufferMs, bufferForPlaybackMs, bufferForPlaybackAfterRebufferMs).setPrioritizeTimeOverSizeThresholds(true) // 优先满足时间要求.createDefaultLoadControl();// 应用到ExoPlayerExoPlayer player = new ExoPlayer.Builder(context).setLoadControl(loadControl).build();
关键点:
minBufferMs、maxBufferMs 等参数需根据网络状况动态调整。MediaSource。ExoPlayer 提供了默认的播放控制UI,但通常需要自定义以匹配应用风格。
// 自定义PlayerViewPlayerView playerView = findViewById(R.id.player_view);playerView.setControllerVisibilityListener(visibility -> {// 处理控制栏显示/隐藏逻辑});// 完全自定义控制栏// 1. 继承PlayerControlViewpublic class CustomPlayerControlView extends PlayerControlView {// 实现自定义布局和逻辑}// 2. 在布局文件中使用<com.example.CustomPlayerControlViewandroid:id="@+id/custom_control_view"android:layout_width="match_parent"android:layout_height="wrap_content" />
关键点:
onPlayerStateChanged、onPlayWhenReadyChanged 等方法以响应播放状态变化。Android 8.0+ 支持画中画模式,ExoPlayer 无需额外配置即可兼容。
// 进入画中画模式if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {PictureInPictureParams.Builder builder = new PictureInPictureParams.Builder();// 可选:设置自定义动作playerView.enterPictureInPictureMode(builder.build());}
关键点:
AndroidManifest.xml 中声明 android.permission.FOREGROUND_SERVICE。onPictureInPictureModeChanged 以更新UI。ExoPlayer 默认支持多种格式,但可通过 MediaCodecSelector 自定义解码器。
// 强制使用特定解码器(示例:禁用硬件解码)MediaCodecSelector mediaCodecSelector = new MediaCodecSelector() {@Overridepublic List<MediaCodecInfo> getDecoderInfos(String mimeType, boolean requiresSecureDecoder) throws DecoderQueryException {List<MediaCodecInfo> decoderInfos = MediaCodecSelector.DEFAULT.getDecoderInfos(mimeType, requiresSecureDecoder);// 过滤硬件解码器(示例逻辑)decoderInfos.removeIf(info -> info.isHardwareAccelerated());return decoderInfos;}};ExoPlayer player = new ExoPlayer.Builder(context).setMediaCodecSelector(mediaCodecSelector).build();
关键点:
对于受DRM保护的内容(如Widevine),需配置 DrmSessionManager。
// 创建Widevine DRM会话管理器DrmSessionManager<FrameworkMediaCrypto> drmSessionManager = new DefaultDrmSessionManager.Builder().setUuidAndExoMediaDrmProvider(C.WIDEVINE_UUID, FrameworkMediaDrm.DEFAULT_PROVIDER).build(new DefaultDrmSessionManager.DrmSessionEventListener() {@Overridepublic void onDrmSessionReleased() {// 处理DRM会话释放}});// 应用到MediaSourceMediaSource mediaSource = new ProgressiveMediaSource.Factory(dataSourceFactory).setDrmSessionManager(drmSessionManager).createMediaSource(MediaItem.fromUri(contentUri));
关键点:
HttpMediaDrmCallback 以获取DRM许可证。onKeyRequired 事件以传递许可证URL。通过 Player.EventListener 捕获播放错误。
player.addListener(new Player.Listener() {@Overridepublic void onPlayerError(PlaybackException error) {// 分类处理错误if (error.type == PlaybackException.TYPE_SOURCE) {Log.e("ExoPlayer", "源错误: " + error.getMessage());} else if (error.type == PlaybackException.TYPE_RENDERER) {Log.e("ExoPlayer", "渲染错误: " + error.getMessage());}// 尝试恢复或显示错误UI}});
关键点:
TYPE_SOURCE、TYPE_RENDERER 等以精准定位问题。player.prepare(mediaSource) 重试。启用ExoPlayer的详细日志以辅助调试。
// 在Application中初始化时设置ExoPlayerLibraryInfo.setLogLevel(Log.DEBUG);
关键点:
ExoPlayer 标签以查看关键事件(如缓冲、解码)。通过本篇的深入探讨,我们掌握了ExoPlayer在网络优化、自定义UI、多格式支持及错误处理等方面的高级用法。未来,随着Media3的持续演进,ExoPlayer将进一步简化复杂音视频场景的开发,例如:
开发者应持续关注Media3的更新日志,及时将新特性集成到项目中,以保持竞争力。