ExoPlayer架构详解与源码分析(1)——前言
ExoPlayer架构详解与源码分析(2)——Player
ExoPlayer架构详解与源码分析(3)——Timeline
ExoPlayer架构详解与源码分析(4)——整体架构
ExoPlayer架构详解与源码分析(5)——MediaSource
ExoPlayer架构详解与源码分析(6)——MediaPeriod
ExoPlayer架构详解与源码分析(7)——SampleQueue
上篇说完整体架构,这里开始分析其中的各个组件,先从MediaSource看起,继续拿运载火箭做对比,MediaSource在整个运载火箭中的角色就类似于燃料系统,确保火箭顺利升空,燃料系统是其中重要的一环,需要能在运行过程中持续稳定的提供燃料。ExoPlayer也一样,为了保证能够持续的渲染出媒体内容,就得保证MediaSource持续稳定提供需要的数据。
继续扩充下我们的版图
MediaSource定义了媒体信息以及提供媒体数据给播放器,主要有2个职责:
应用代码不应该直接调用MediaSource 里的方法,而应该让ExoPlayer在合适的时间调用。
MediaSource实例可以重复使用,但只能同时用于一个 ExoPlayer 实例。
不同MediaSource 方法只能在应用程序线程或内部播放线程其中一个上调用。每个方法文档上都明确了可以调用的线程。
看下几个重要的方法定义
void prepareSource(
MediaSourceCaller caller,
@Nullable TransferListener mediaTransferListener,
PlayerId playerId);
interface MediaSourceCaller {
void onSourceInfoRefreshed(MediaSource source, Timeline timeline);
}
MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs);
MeidiaSource大致工作流程就是创建时初始化出一个Timeline,然后prepareSource准备数据源,之后createPeriod创建Period,然后讲工作交给Period,整个过程都在刷新Timeline。
MediaSource虚函数实现,主要用于多个MediaSourceEventListener的处理分发,触发多个MediaSourceCaller的onSourceInfoRefreshed,还保存上一次的Timeline。
由多个子MediaSource组成的复合MediaSource,将所有方法调用转发给各个子的MediaSource
继承自CompositeMediaSource,实现只包含了一个子MediaSource的MediaSource 。
一个MediaSource ,主要作用是当实际媒体结果未知时,用一个PlaceholderTimeline来表示Timeline ,当获取实际的媒体结构时采用实际的Timeline替换PlaceholderTimeline。
public MaskingMediaSource(MediaSource mediaSource, boolean useLazyPreparation) {
super(mediaSource);
this.useLazyPreparation = useLazyPreparation && mediaSource.isSingleWindow();
window = new Timeline.Window();
period = new Timeline.Period();
@Nullable Timeline initialTimeline = mediaSource.getInitialTimeline();
if (initialTimeline != null) {
timeline =
MaskingTimeline.createWithRealTimeline(
initialTimeline, /* firstWindowUid= */ null, /* firstPeriodUid= */ null);
hasRealTimeline = true;
} else {
timeline = MaskingTimeline.createWithPlaceholderTimeline(mediaSource.getMediaItem());
}
}
继承自BaseMediaSource,主要用于渐进式媒体文件的播放,如本地或远程的单个视频文件,ProgressiveMediaSource也是最基本的MediaSource,比较典型是后面重点讲解的对象,剩余其他几种类型的可以先做了解,后面有时间再扩充。
@Override
//prepare
protected void prepareSourceInternal(@Nullable TransferListener mediaTransferListener) {
transferListener = mediaTransferListener;
drmSessionManager.setPlayer(
/* playbackLooper= */ checkNotNull(Looper.myLooper()), getPlayerId());
drmSessionManager.prepare();
notifySourceInfoRefreshed();
}
@Override
//ProgressiveMediaPeriod在获取到Timeline相关信息后会回调更新Timeline
public void onSourceInfoRefreshed(long durationUs, boolean isSeekable, boolean isLive) {
// 优先实现之前的durationUs
durationUs = durationUs == C.TIME_UNSET ? timelineDurationUs : durationUs;
if (!timelineIsPlaceholder
&& timelineDurationUs == durationUs
&& timelineIsSeekable == isSeekable
&& timelineIsLive == isLive) {
// 没有发生变更
return;
}
timelineDurationUs = durationUs;
timelineIsSeekable = isSeekable;
timelineIsLive = isLive;
timelineIsPlaceholder = false;
notifySourceInfoRefreshed();
}
//刷新TimeLine
private void notifySourceInfoRefreshed() {
Timeline timeline =
new SinglePeriodTimeline(
timelineDurationUs,
timelineIsSeekable,
/* isDynamic= */ false,
/* useLiveConfiguration= */ timelineIsLive,
/* manifest= */ null,
mediaItem);
if (timelineIsPlaceholder) {
timeline =
new ForwardingTimeline(timeline) {
@Override
public Window getWindow(
int windowIndex, Window window, long defaultPositionProjectionUs) {
super.getWindow(windowIndex, window, defaultPositionProjectionUs);
window.isPlaceholder = true;
return window;
}
@Override
public Period getPeriod(int periodIndex, Period period, boolean setIds) {
super.getPeriod(periodIndex, period, setIds);
period.isPlaceholder = true;
return period;
}
};
}
//触发监听
refreshSourceInfo(timeline);
}
没了就这么多,燃料系统这么简陋的吗?当然不会,因为它把除了Timeline的管理维护之外的几乎所有的工作都交给别人来完成了,它就是下面要重点讲的MediaPeriod,MediaSource只管创建出就好了,ExoPlayer也是主要通过MediaSource关联的MediaPeriod控制媒体的加载释放等。
版权声明 ©
本文为CSDN作者山雨楼原创文章
转载请注明出处
原创不易,觉得有用的话,收藏转发点赞支持