Jetpack - Media3(ExoPlayer 播放器控制)
官方页面一、概念1.1 架构Player 播放器ExoPlayer 是具体实现。MediaSession 媒体会话协调 MediaController 和 Player。MediaController 媒体控制器用于跨组件或外部远程控制媒体播放耳机、手表、电视、汽车、谷歌助理。MediaSessionService用于支持跨组件播放的Service。(ExoPlayer支持处于后台时播放)1.2 协议支持DASHHLSSmoothStreaming全称Dynamic Adaptive Streaming over HTTPHTTP Live StreamingIIS Smooth Streaming主导方MPEG国际标准苹果微软清单文件格式基于XML的 .mpd基于文本的 .m3u8基于XML的 .ismc现状业界事实标准之一更开放、灵活业界事实标准之一兼容性极佳遗产协议仍在某些系统使用RTSP/RTPHLS / DASH / Smooth Streaming协议控制协议 数据传输协议基于HTTP的应用协议典型应用IP摄像头、视频会议、专网监控互联网视频网站Netflix/YouTube、公共直播MIDI音频文件播放MP3, WAV等文件内容指令集乐谱声音波形的编码录音最终音质取决于播放设备的音源取决于文件本身的编码和比特率二、简单使用2.1 添加依赖最新版本[versions] media3 1.9.0 [libraries] media3-exoplayer { module androidx.media3:media3-exoplayer, version.ref media3 } media3-datasource-okhttp { module androidx.media3:media3-datasource-okhttp, version.ref media3 } media3-ui { module androidx.media3:media3-ui, version.ref media3 } //可选使用 MediaSession media3-session { module androidx.media3:media3-session, version.ref media3 }可选协议支持//DASH media3-dash { module androidx.media3:media3-exoplayer-dash, version.ref media3 } //HLS media3-hls { module androidx.media3:media3-exoplayer-hls, version.ref media3 } //SmoothStreaming media3-smoothstreaming { module androidx.media3:media3-exoplayer-smoothstreaming, version.ref media3 } //RTSP media3-rtsp { module androidx.media3:media3-exoplayer-rtsp, version.ref media3 } //MIDI media3-midi { module androidx.media3:media3-exoplayer-midi, version.ref media3 } //For ad insertion using the Interactive Media Ads SDK with ExoPlayer media3-ima { module androidx.media3:media3-exoplayer-ima, version.ref media3 }2.2 添加控件 PlayerView可用于视频、图片和音频播放。在播放视频时它会呈现视频和字幕在播放图片时它会呈现位图并且可以显示音频文件中作为元数据包含的封面图片。androidx.media3.ui.PlayerView android:idid/playerView android:layout_widthmatch_parent android:layout_heightmatch_parent android:keepScreenOntrue /Activity { override fun onStart() { super.onResume() playerView.onResume() } override fun onStop() { super.onStop() playerView.onPause() } }2.3 创建播放器 ExoPlayer每次快进快退的时间public Builder setSeekBackIncrementMs(IntRange(from 1) long seekBackIncrementMs)public Builder setSeekForwardIncrementMs(IntRange(from 1) long seekForwardIncrementMs)val player ExoPlayer.Builder(APP.context) .setSeekBackIncrementMs(10000) //每次快退的时间10秒 .setSeekForwardIncrementMs(10000) //每次快进的时间10秒 .build() playerView.player player //将播放器和控件绑定 player.prepare() playWhenReady true2.4 添加媒体项 MediaItem通过 Uri 、Bundle 指定播放文件。播放期间可以更新播放列表无需再次准备播放器。public static MediaItem fromUri(String uri)public static MediaItem fromUri(Uri uri)public static MediaItem fromBundle(Bundle bundle)//创建播放内容 val mediaItem MediaItem.fromUri() //将播放内容添加到播放器中 player.setMediaItem(mediaItem)2.6 控制播放器初始化void prepare();将使播放器脱离空闲状态开始加载媒体内容并获取播放所需的系统资源。播放void play();暂停void pause();释放播放器void release();释放播放器实例。当不再需要使用播放器时必须调用此方法。调用此方法后严禁继续使用该播放器实例。自动播放boolean getPlayWhenReady();void setPlayWhenReady(boolean playWhenReady);播放器就绪状态时是否自动播放。播放速度void setPlaybackSpeed(FloatRange(from 0, fromInclusive false) float speed);调整速率不改变音调参数 speed 必须大于 01.0 为正常速率2.0 为两倍速0.5 为半速。状态判断boolean isPlaying();是否正在播放中。boolean isLoading();是否正在加载资源。跳转void seekToPreviousMediaItem();void seekToNextMediaItem();跳转到上一个或下一个媒体项。boolean hasPreviousMediaItem();boolean hasNextMediaItem();是否有上一个或下一个媒体项。void seekTo(int mediaItemIndex, long positionMs);跳转至指定媒体项中由毫秒数定义的播放位置。进度条void seekBack();void seekForward();将当前媒体项的播放位置向前或向后跳到创建播放器时通过 setSeekBackIncrement()或setSeekForwardIncrement() 所定义的毫秒值。void seekTo(long positionMs);跳转至当前媒体项中指定的毫秒数位置。音量增益float getVolume();void setVolume(FloatRange(from 0, to 1.0) float volume);设置音频输出增益。有效取值范围为 0静音至 1单位增益保持原信号的闭区间。三、监听事件可以通过 player.addListener() 或挂起函数 player.listen{} 来监听。Player.Listener 没有必须实现的方法只需要实现感兴趣的。状态更改default void onPlaybackStateChanged(State int playbackState) {}分别有空闲 Player.STATE_IDLE、缓冲中 Player.STATE_BUFFERING、就绪 Player.STATE_READY、播放完 Player.STATE_ENDED。也可以直接通过 player.playbackState 获取。是否在播放default void onIsPlayingChanged(boolean isPlaying) {}也可以直接用通过 player.isPlaying 获取。播放错误default void onPlayerError(PlaybackException error) {}player.addListener(object : Player.Listener { //TODO }) player.listen { event - //TODO }3.1 状态更改 onPlaybackStateChanged()override fun onPlaybackStateChanged(playbackState: Int) { when(playbackState) { //空闲 Player.STATE_IDLE - {} //缓冲中 Player.STATE_BUFFERING - {} //就绪 Player.STATE_READY - {} //播放完 Player.STATE_ENDED - {} } }3.2 是否在播放 onIsPlayingChanged()override fun onIsPlayingChanged(isPlaying: Boolean) { if (isPlaying) {} else {} }3.3 播放错误 onPlayerError()override fun onPlayerError(error: PlaybackException) { when (error.cause) { //http错误 is HttpDataSource.HttpDataSourceException - {} else - {} } when (error.errorCode) { //网络错误 PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED - {} //解码错误 PlaybackException.ERROR_CODE_DECODING_FAILED - {} else - {} } }3.4 播放列表/媒体源变化 onTimelineChanged()override fun onTimelineChanged(timeline: Timeline, reason: Int) { when(reason) { //播放列表发生变化时 Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED - {} //媒体源更新而发生变化 Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE - {} } }3.5 切歌 onMediaItemTransition()override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {}3.6 监听播放进度如果需要设置进度条Listener 中没有对应的回调用来持续监听播放进度需要手动实现按一定时间间隔去查询。val currentPosition player.currentPosition val duration player.duration seekBar.progress if (duration 0) { (currentPosition * 100 / duration).toInt() } else 0 currentText formatTime(currentPosition) durationText formatTime(duration)四、媒体项 MediaItem还可以通过静态方法 MediaItem.fromUri() 创建但是创建后不能设置元信息20260408通过 Builder 模式构建实例。MediaItem 会由 MediaSource.Factory 转换为可播放的 MediaSource。如果未进行自定义配置此转换将由 DefaultMediaSourceFactory 执行它能够构建与媒体内容的属性对应的复杂媒体源。设置Uripublic Builder setUri(Nullable Uri uri)public Builder setUri(Nullable String uri)设置媒体资源。设置IDpublic Builder setMediaId(String mediaId)用于识别媒体项。设置Tagpublic Builder setTag(Nullable Object tag)为媒体项设置一个自定义代码可以是任意对象一般用于元数据。获取元数据public final MediaMetadata mediaMetadata;通过属性获取媒体项的元数据包含了媒体文件类型、作者、专辑、时长等。注意MediaItem 只是一个播放清单的条目不会自动从 Uri 中解析元数据需要在创建时通过 setMediaMetadata()手动添加。val mediaItem MediaItem.Builder() .setUri() .setTag() .setMediaId() .build()4.1 串流ExoPlayer 为 DASH、HLS 和 SmoothStreaming 提供自适应媒体源。如果此类自适应媒体内容的 URI 以标准文件扩展名结尾系统会自动创建相应的媒体源。如果 URI 具有非标准扩展名或没有扩展名则可以明确设置 MIME 类型以指明媒体内容的类型。对于渐进式媒体串流不需要 MIME 类型。便捷创建串流媒体项public static MediaItem fromUri(String uri)public static MediaItem fromUri(Uri uri)快捷构建仅包含串流 URI 的媒体项。设置自适应媒体的类型public Builder setMimeType(Nullable String mimeType)val mediaItem MediaItem.fromUri() .setMimeType(MimeTypes.APPLICATION_M3U8)4.2 图像播放图片媒体项中必须包含时长以指定图片在播放期间应显示的时长。图片播放时常public Builder setImageDurationMs(long imageDurationMs)val mediaItem MediaItem.Builder() .setUri(imageUri) .setImageDurationMs(3000) .build()4.3 受保护的内容对于受保护的内容应设置媒体内容的 DRM 属性。UUID 是必需的所有其他属性都是可选的。val mediaItem MediaItem.Builder() .setUri() .setDrmConfiguration( MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID) .setLicenseUri() .setMultiSession(true) .setLicenseRequestHeaders(mapOf( to )) .build() ).build()五、播放列表播放列表中的项之间可以无缝切换无格式相同要求甚至无媒体类型相同要求也就是播放列表可以同时包含图片、视频、音频。5.1 增删改查清空并添加void setMediaItem(MediaItem mediaItem);void setMediaItems(ListMediaItem mediaItems);播放位置重置为起始点。void setMediaItem(MediaItem mediaItem, boolean resetPosition);void setMediaItems(ListMediaItem mediaItems, boolean resetPosition);参数resetPosition设为flase不会重置播放位置。void setMediaItem(MediaItem mediaItem, long startPositionMs);void setMediaItems(ListMediaItem mediaItems, int startIndex, long startPositionMs);参数startPositionMs以毫秒为单位的播放起始点。参数startIndex起始索引。添加void addMediaItem(MediaItem mediaItem);void addMediaItems(ListMediaItem mediaItems);添加到列表尾部。void addMediaItem(int index, MediaItem mediaItem);void addMediaItems(int index, ListMediaItem mediaItems);指定索引处插入。移动void moveMediaItem(int currentIndex, int newIndex);将当前索引 currentIndex 的条目移动到目标索引 newIndex。currentIndex超过范围请求会被忽略newIndex超过范围则为列表尾部。void moveMediaItems(int fromIndex, int toIndex, int newIndex);将起始索引 fromIndex(包含) 到目标索引 toIndex(不包含) 之间的条目移动到目标索引newIndex。fromIndex超出范围请求会被忽略toIndex超出范围则一直截取到尾部newIndex超出截取后剩余的范围则为列表尾部。替换void replaceMediaItem(int index, MediaItem mediaItem);void replaceMediaItems(int fromIndex, int toIndex, ListMediaItem mediaItems);将起始索引fromIndex(包含)到目标索引toIndex(不包含) 之间的条目替换成目标集合mediaItems。fromIndex超出范围请求会被忽略toIndex超出范围则一直截取到尾部。移除void removeMediaItem(int index);void removeMediaItems(int fromIndex, int toIndex);移除起始索引fromIndex(包含)到目标索引toIndex(不包含) 之间的条目。fromIndex超出范围请求会被忽略toIndex超出范围则一直截取到尾部。清空void clearMediaItems();查找int getCurrentMediaItemIndex();当前播放的媒体项索引。MediaItem getCurrentMediaItem();当前播放的媒体项。5.2 重复模式 RepeatMode重复模式int getRepeatMode();void setRepeatMode(RepeatMode int repeatMode);循环关 REPEAT_MODE_OFF、单循环 REPEAT_MODE_ONE、列表循环 REPEAT_MODE_ALL。5.3 随机播放模式 ShuffleMode随机播放boolean getShuffleModeEnabled();void setShuffleModeEnabled(boolean shuffleModeEnabled);所有内容都会播放一次。自定义顺序public DefaultShuffleOrder(int[] shuffledIndices, long randomSeed)可以通过提供自定义重排顺序实现或 在 DefaultShuffleOrder 构造函数中设置自定义顺序。参数shuffledIndices作为播放顺序的乱序数组参数randomSeed是随机数种子。六、后台播放 MediaSession