关于PlayerBase的使用说明和项目源码请参考
Android播放器基础封装库PlayerBase
GitHub源码
顺便说一句:感觉这个PlayerBase真的是与众不同。膜拜大神的杰作。
其实对于这种无缝的续播,原理很简单。就是一个解码实例更换不同的渲染视图即可。可以简单比作一个MediaPlayer去不断设置不同的surface呈现播放。如果自己处理这个过程的话想对比较繁琐,你需要处理Render的回调并关联给解码器,还需要自己处理Render的测量以及显示比例、角度等等问题。
RelationAssist 就是为了简化这个过程而设计的。在不同的页面或视图切换播放时,您只需要提供并传入对应位置的视图容器(ViewGroup类型)即可。内部复杂的设置项和关联由RelationAssist完成。
PlayerBase中提供了一个关联助手(RelationAssist)
可以用来实现视频跨页面无缝衔接的效果。但是官方demo中并没有提供相关的源码示例,今天就使用PlayerBase实现这个效果。
先看个效果图,视频转gif使用的工具是 gifrocket
1.新建App,进行必要的初始化。
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
//初始化播放器,默认使用系统的MediaPlayer
PlayerLibrary.init(this);
}
}
- 新建RelationAssistSingleton类,维护RelationAssist单个实例
public class RelationAssistSingleton {
private static RelationAssist mAssist;
private RelationAssistSingleton() {
}
public static RelationAssist getAssist(Context context) {
if (mAssist == null) {
synchronized (RelationAssistSingleton.class) {
if (mAssist == null) {
mAssist = new RelationAssist(context);
}
}
}
return mAssist;
}
public static void releaseAssist() {
if (mAssist != null) {
mAssist.destroy();
mAssist = null;
}
}
}
- 新建TestToDetailActivity,从这界面跳转到详情界面。
- 布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.TestToDetailActivity">
<FrameLayout
android:id="@+id/flVideoContainer"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#FFFFFF" />
<Button
android:id="@+id/btnToDetail"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="跳转至详情界面无缝播放" />
</LinearLayout>
- 代码
public class TestToDetailActivity extends AppCompatActivity {
private static final String TAG = "TestToDetailActivity";
//播放视频的container
private FrameLayout flVideoContainer;
private Button btnToDetail;
private RelationAssist mAssist;
private ReceiverGroup receiverGroup;
//用来接收播放过程中产生的事件,可以参考官方demo
private OnAssistPlayEventHandler eventHandler = new OnAssistPlayEventHandler() {
@Override
public void onAssistHandle(AssistPlay assist, int eventCode, Bundle bundle) {
super.onAssistHandle(assist, eventCode, bundle);
switch (eventCode) {
case DataInter.Event.EVENT_CODE_REQUEST_BACK:
onBackPressed();
break;
case DataInter.Event.EVENT_CODE_ERROR_SHOW:
mAssist.stop();
break;
default:
break;
}
}
};
public static void launch(Context context) {
Intent intent = new Intent(context, TestToDetailActivity.class);
context.startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_to_detail);
//保持屏幕常亮
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
flVideoContainer = findViewById(R.id.flVideoContainer);
btnToDetail = findViewById(R.id.btnToDetail);
btnToDetail.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//跳转到另一个播放界面,注意这里我们使用的是startActivityForResult
Intent intent = new Intent(TestToDetailActivity.this,
DetailSeamlessPlayActivity.class);
startActivityForResult(intent, 100);
}
});
//获取并初始化关联助手
mAssist = RelationAssistSingleton.getAssist(getApplicationContext());
mAssist.getSuperContainer().setBackgroundColor(Color.BLACK);
mAssist.setEventAssistHandler(eventHandler);
receiverGroup = ReceiverGroupManager.get().getLiteReceiverGroup(this);
receiverGroup.getGroupValue().putBoolean(DataInter.Key.KEY_NETWORK_RESOURCE, true);
//注册事件接收组
mAssist.setReceiverGroup(receiverGroup);
//设置播放源
DataSource dataSource = new DataSource();
dataSource.setData("https://mov.bn.netease.com/open-movie/nos/mp4/2016/01/11/SBC46Q9DV_hd.mp4");
dataSource.setTitle("神奇的珊瑚");
mAssist.setDataSource(dataSource);
//默认标记用户是手动播放的,标记此变量的作用是为了:
//如果用户在一个界面手动暂停播放了,那么跳转到另一个界面的时候应该保持暂停状态。此变量默认是true。
mAssist.getReceiverGroup().getGroupValue().putBoolean(GroupValueMap.USER_START_PLAY, true);
//关联播放容器并开始播放。
mAssist.attachContainer(flVideoContainer);
mAssist.play();
}
@Override
protected void onResume() {
Log.d(TAG, "onResume: ");
super.onResume();
int state = mAssist.getState();
if (state == IPlayer.STATE_PLAYBACK_COMPLETE)
return;
//onResume的时候判断用户是否手动播放了,如果是手动播放则继续播放。
//如果用户手动暂停了,则暂停播放。
boolean userStart = receiverGroup.getGroupValue().getBoolean(GroupValueMap.USER_START_PLAY);
if (mAssist.isInPlaybackState() && userStart) {
mAssist.resume();
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Log.d(TAG, "onActivityResult: ");
//从另一个播放界面返回的时候,重新关联当前界面的播放容器。
mAssist.attachContainer(flVideoContainer);
}
@Override
protected void onPause() {
super.onPause();
int state = mAssist.getState();
if (state == IPlayer.STATE_PLAYBACK_COMPLETE)
return;
if (mAssist.isInPlaybackState()) {
mAssist.pause();
} else {
mAssist.stop();
}
}
@Override
protected void onDestroy() {
//释放RelationAssist
RelationAssistSingleton.releaseAssist();
mAssist = null;
super.onDestroy();
}
}
- 新建DetailSeamlessPlayActivity,这个界面充当详情界面。
- 布局文件
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.DetailSeamlessPlayActivity">
<FrameLayout
android:id="@+id/flDetailVideoContainer"
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_centerVertical="true"
android:background="#FFFFFF" />
</RelativeLayout>
- 代码
public class DetailSeamlessPlayActivity extends AppCompatActivity {
private FrameLayout flDetailVideoContainer;
private RelationAssist mAssist;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail_seamless_play);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
flDetailVideoContainer = findViewById(R.id.flDetailVideoContainer);
//获取关联助手,关联播放容器。
mAssist = RelationAssistSingleton.getAssist(getApplicationContext());
mAssist.attachContainer(flDetailVideoContainer);
}
@Override
protected void onResume() {
super.onResume();
int state = mAssist.getState();
if (state == IPlayer.STATE_PLAYBACK_COMPLETE)
return;
//onResume的时候判断用户是否手动播放了,如果是手动播放则继续播放。
//如果用户手动暂停了,则暂停播放。
boolean userStart = mAssist.getReceiverGroup().getGroupValue().getBoolean(GroupValueMap.USER_START_PLAY);
if (mAssist.isInPlaybackState() && userStart) {
mAssist.resume();
}
}
@Override
protected void onPause() {
super.onPause();
int state = mAssist.getState();
if (state == IPlayer.STATE_PLAYBACK_COMPLETE)
return;
if (mAssist.isInPlaybackState()) {
mAssist.pause();
} else {
mAssist.stop();
}
}
}
RelationAssist源码分析
RelationAssist的继承结构
public final class RelationAssist implements AssistPlay {
//...
}
AssistPlay接口
public interface AssistPlay {
void setOnPlayerEventListener(OnPlayerEventListener onPlayerEventListener);
void setOnErrorEventListener(OnErrorEventListener onErrorEventListener);
void setOnReceiverEventListener(OnReceiverEventListener onReceiverEventListener);
void setOnProviderListener(IDataProvider.OnProviderListener onProviderListener);
void setDataProvider(IDataProvider dataProvider);
boolean switchDecoder(int decoderPlanId);
void setRenderType(int renderType);
void setAspectRatio(AspectRatio aspectRatio);
void setVolume(float left, float right);
void setSpeed(float speed);
void setReceiverGroup(IReceiverGroup receiverGroup);
void attachContainer(ViewGroup userContainer);
void setDataSource(DataSource dataSource);
void play();
void play(boolean updateRender);
boolean isInPlaybackState();
boolean isPlaying();
int getCurrentPosition();
int getDuration();
int getAudioSessionId();
int getBufferPercentage();
int getState();
void rePlay(int msc);
void pause();
void resume();
void seekTo(int msc);
void stop();
void reset();
void destroy();
}
为了简单,在下文中使用A表示启动详情界面的界面(TestToDetailActivity),B界面表示详情界面(DetailSeamlessPlayActivity)。
先看一下A界面的关键代码
//要播放视频的布局
flVideoContainer = findViewById(R.id.flVideoContainer);
//获取RelationAssist实例
mAssist = RelationAssistSingleton.getAssist(getApplicationContext());
//构建接收者组
receiverGroup = ReceiverGroupManager.get().getLiteReceiverGroup(this);
//RelationAssist设置接收者组,内部会根据优先级进行排序
mAssist.setReceiverGroup(receiverGroup);
//构建播放资源
DataSource dataSource = new DataSource();
dataSource.setData("https://mov.bn.netease.com/open-movie/nos/mp4/2016/01/11/SBC46Q9DV_hd.mp4");
dataSource.setTitle("神奇的珊瑚");
//RelationAssist设置播放资源
mAssist.setDataSource(dataSource);
//关联要播放视频的View
mAssist.attachContainer(flVideoContainer);
//开始播放
mAssist.play();
首先我们要获取RelationAssist实例,我们看一下RelationAssist类部分代码和构造方法。
public final class RelationAssist implements AssistPlay {
private final String TAG = "RelationAssist";
private Context mContext;
//播放器
private AVPlayer mPlayer;
/**
* SuperContainer for ReceiverGroup and Render.
*/
private SuperContainer mSuperContainer;
/**
* ReceiverGroup from out setting.
*/
private IReceiverGroup mReceiverGroup;
//默认的渲染类型,我们选择SurfaceView
private int mRenderType = IRender.RENDER_TYPE_SURFACE_VIEW;
private boolean mRenderTypeChange;
private IRender mRender;
//视频画面缩放模式
private AspectRatio mAspectRatio = AspectRatio.AspectRatio_FIT_PARENT;
private int mVideoWidth;
private int mVideoHeight;
private int mVideoSarNum;
private int mVideoSarDen;
private int mVideoRotation;
private IRender.IRenderHolder mRenderHolder;
private DataSource mDataSource;
private boolean isBuffering;
private OnPlayerEventListener mOnPlayerEventListener;
private OnErrorEventListener mOnErrorEventListener;
private OnReceiverEventListener mOnReceiverEventListener;
private OnAssistPlayEventHandler mOnEventAssistHandler;
public RelationAssist(Context context){
this(context, null);
}
public RelationAssist(Context context, SuperContainer superContainer){
this.mContext = context;
//初始化播放器
mPlayer = new AVPlayer();
//初始化超级容器
if(superContainer == null){
superContainer = new SuperContainer(context);
}
//默认是 false
if(PlayerConfig.isUseDefaultNetworkEventProducer())
superContainer.addEventProducer(new NetworkEventProducer(context));
mSuperContainer = superContainer;
mSuperContainer.setStateGetter(mInternalStateGetter);
}
}
在RelationAssist的构造函数中,我们初始化了超级容器,我们进去看一下SuperContainer的构造函数
public SuperContainer(Context context) {
super(context);
init(context);
}
private void init(Context context) {
initBaseInfo(context);
initGesture(context);
//初始化渲染容器
initRenderContainer(context);
//初始化cover容器
initReceiverContainer(context);
}
初始化渲染容器mRenderContainer,这个容器是最先加入到SuperContainer中的,在视图的最底层,用来渲染视频。
private void initRenderContainer(Context context) {
//用来渲染视频
mRenderContainer = new FrameLayout(context);
addView(mRenderContainer,
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
}
初始化cover容器,cover容器是在渲染视图之上的。
private void initReceiverContainer(Context context) {
mCoverStrategy = getCoverStrategy(context);
addView(mCoverStrategy.getContainerView(),
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
}
protected ICoverStrategy getCoverStrategy(Context context){
return new DefaultLevelCoverContainer(context);
}
DefaultLevelCoverContainer类部分代码,这个类里面包括 low,medium,high三种级别的容器用来放置不同级别的cover。
public class DefaultLevelCoverContainer extends BaseLevelCoverContainer {
private final String TAG = "DefaultLevelCoverContainer";
/**
* the low level covers container .
*/
private FrameLayout mLevelLowCoverContainer;
/**
* the medium level covers container .
*/
private FrameLayout mLevelMediumCoverContainer;
/**
* the high level covers container .
*/
private FrameLayout mLevelHighCoverContainer;
public DefaultLevelCoverContainer(Context context) {
super(context);
}
@Override
protected void initLevelContainers(Context context) {
//add low cover container
mLevelLowCoverContainer = new FrameLayout(context);
mLevelLowCoverContainer.setBackgroundColor(Color.TRANSPARENT);
addLevelContainerView(mLevelLowCoverContainer,null);
//add medium cover container
mLevelMediumCoverContainer = new FrameLayout(context);
mLevelMediumCoverContainer.setBackgroundColor(Color.TRANSPARENT);
addLevelContainerView(mLevelMediumCoverContainer,null);
//add high cover container
mLevelHighCoverContainer = new FrameLayout(context);
mLevelHighCoverContainer.setBackgroundColor(Color.TRANSPARENT);
addLevelContainerView(mLevelHighCoverContainer,null);
}
//...
}
A界面中的代码,关联要播放视频的View
//注释1处关联要播放视频的View
mAssist.attachContainer(flVideoContainer);
@Override
public void attachContainer(ViewGroup userContainer) {
attachContainer(userContainer, false);
}
public void attachContainer(ViewGroup userContainer, boolean updateRender){
attachPlayerListener();
//注释2处,先从超级容器中分离
detachSuperContainer();
if(mReceiverGroup!=null){
mSuperContainer.setReceiverGroup(mReceiverGroup);
}
//第一次调用的时候isNeedForceUpdateRender()为true
if(updateRender || isNeedForceUpdateRender()){
//释放渲染器
releaseRender();
//注释3处,更新渲染器
updateRender();
}
//注释4处,连接SuperContainer
if(userContainer!=null){
userContainer.addView(mSuperContainer,
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
}
}
RelationAssist的detachSuperContainer方法
private void detachSuperContainer(){
ViewParent parent = mSuperContainer.getParent();
if(parent!=null && parent instanceof ViewGroup){
((ViewGroup) parent).removeView(mSuperContainer);
}
}
RelationAssist的updateRender方法
private void updateRender(){
if(isNeedForceUpdateRender()){
mRenderTypeChange = false;
releaseRender();
//根据类型创建SurfaceView或者TextureView
switch (mRenderType){
case IRender.RENDER_TYPE_SURFACE_VIEW:
mRender = new RenderSurfaceView(mContext);
break;
case IRender.RENDER_TYPE_TEXTURE_VIEW:
default:
mRender = new RenderTextureView(mContext);
((RenderTextureView)mRender).setTakeOverSurfaceTexture(true);
break;
}
mRenderHolder = null;
//播放器设置 surface为null
mPlayer.setSurface(null);
//设置渲染比例
mRender.updateAspectRatio(mAspectRatio);
mRender.setRenderCallback(mRenderCallback);
//update some params for render type change
mRender.updateVideoSize(mVideoWidth, mVideoHeight);
mRender.setVideoSampleAspectRatio(mVideoSarNum, mVideoSarDen);
//update video rotation
mRender.setVideoRotation(mVideoRotation);
//将渲染视图加入到mSuperContainer
mSuperContainer.setRenderView(mRender.getRenderView());
}
}
SuperContainer的setRenderView方法
public final void setRenderView(View view){
removeRender();
//must set WRAP_CONTENT and CENTER for render aspect ratio and measure.
LayoutParams lp = new LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT,
Gravity.CENTER);
//添加渲染视图,这里我们选择的是RenderSurfaceView
mRenderContainer.addView(view,lp);
}
然后回到注释4处,将mSuperContainer
添加到我们传入的要播放视频的布局
if(userContainer!=null){
userContainer.addView(mSuperContainer, new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
}
到这里,我们我们已经把要播放视频的mSuperContainer
添加到我们传入的要播放视频的布局中去了。继续看是怎么播放的。
RelationAssist的play方法
@Override
public void play() {
play(false);
}
@Override
public void play(boolean updateRender) {
if(updateRender){
releaseRender();
updateRender();
}
if(mDataSource!=null){
//设置资源,播放
onInternalSetDataSource(mDataSource);
onInternalStart();
}
}
RelationAssist的onInternalSetDataSource方法
private void onInternalSetDataSource(DataSource dataSource){
//调用AVPlayer的setDataSource方法
mPlayer.setDataSource(dataSource);
}
AVPlayer的setDataSource方法
@Override
public void setDataSource(DataSource dataSource) {
this.mDataSource = dataSource;
//when data source update, attach listener.
initListener();
//if not set DataProvider,will be set data to decoder.
if(!useProvider())
//AVPlayer就是一个包装器,内部的player设置播放资源
interPlayerSetDataSource(dataSource);
}
AVPlayer的interPlayerSetDataSource方法
private void interPlayerSetDataSource(DataSource dataSource){
if(isPlayerAvailable()){
if(isPlayRecordOpen())
mRecordProxyPlayer.onDataSourceReady(dataSource);
//AVPlayer就是一个包装器,内部的player设置播放资源
mInternalPlayer.setDataSource(dataSource);
}
}
我们这里默认的播放器是SysMediaPlayer
,SysMediaPlayer
的setDataSource方法我们暂时不看。
我们回到RelationAssist的onInternalStart方法
private void onInternalStart(){
//播放器开始播放
mPlayer.start();
}
AVPlayer的start方法
@Override
public void start() {
int record = getRecord(mDataSource);
if(useProvider()){
mDataSource.setStartPos(record);
mDataProvider.handleSourceData(mDataSource);
}else{
//调用内置player播放
internalPlayerStart(record);
}
}
SysMediaPlayer的start(int msc)方法
@Override
public void start(int msc) {
if(available()){
if(msc > 0){
startSeekPos = msc;
}
start();
}
}
SysMediaPlayer的start()方法
@Override
public void start() {
try {
if(available() &&
(getState()== STATE_PREPARED
|| getState()== STATE_PAUSED
|| getState()== STATE_PLAYBACK_COMPLETE)){
mMediaPlayer.start();
updateStatus(STATE_STARTED);
//通知开始播放了
submitPlayerEvent(OnPlayerEventListener.PLAYER_EVENT_ON_START, null);
}
}catch (Exception e){
handleException(e);
}
mTargetState = STATE_STARTED;
}
具体怎么播放的我们不去研究了,接下来我们看看进入B界面的时候的代码。
flDetailVideoContainer = findViewById(R.id.flDetailVideoContainer);
mAssist = RelationAssistSingleton.getAssist(getApplicationContext());
//我们仅仅是调用了attachContainer方法而已。
mAssist.attachContainer(flDetailVideoContainer);
我们再看一遍RelationAssist的attachContainer方法
RelationAssist内部会调用到两个参数的重载函数,这个时候对我们来说,最关键的代码就两行。
public void attachContainer(ViewGroup userContainer, boolean updateRender){
attachPlayerListener();
//1. 先把mSuperContainer从上一个布局文件中分离
detachSuperContainer();
if(mReceiverGroup!=null){
mSuperContainer.setReceiverGroup(mReceiverGroup);
}
//此时这个条件不满足
if(updateRender || isNeedForceUpdateRender()){
releaseRender();
//update render view.
updateRender();
}
//2. 重新把mSuperContainer添加到我们传入的新的布局文件中。
if(userContainer!=null){
userContainer.addView(mSuperContainer,
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
}
}
接下来我们要去看一下在重新把mSuperContainer添加到我们传入的新的布局文件中的时候SysMediaPlayer和RenderSurfaceView的一些细节。
public class RenderSurfaceView extends SurfaceView implements IRender {
private IRenderCallback mRenderCallback;
private RenderMeasure mRenderMeasure;
private boolean isReleased;
public RenderSurfaceView(Context context) {
this(context, null);
}
public RenderSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
this.mRenderMeasure = new RenderMeasure();
//监听SurfaceHolder的生命周期
getHolder().addCallback(new InternalSurfaceHolderCallback());
}
@Override
public void setRenderCallback(IRenderCallback renderCallback) {
this.mRenderCallback = renderCallback;
}
private static final class InternalRenderHolder implements IRenderHolder{
private WeakReference<SurfaceHolder> mSurfaceHolder;
public InternalRenderHolder(SurfaceHolder surfaceHolder){
this.mSurfaceHolder = new WeakReference<>(surfaceHolder);
}
@Override
public void bindPlayer(IPlayer player) {
if(player!=null && mSurfaceHolder.get()!=null){
//设置播放器的SurfaceHolder用来显示画面
player.setDisplay(mSurfaceHolder.get());
}
}
}
private class InternalSurfaceHolderCallback implements SurfaceHolder.Callback{
@Override
public void surfaceCreated(SurfaceHolder holder) {
PLog.d(TAG,"<---surfaceCreated---->");
if(mRenderCallback!=null){
mRenderCallback.onSurfaceCreated(new InternalRenderHolder(holder),0,0);
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
PLog.d(TAG,"surfaceChanged : width = " + width + " height = " + height);
if(mRenderCallback!=null){
mRenderCallback.onSurfaceChanged(new InternalRenderHolder(holder),format, width,height);
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
PLog.d(TAG,"***surfaceDestroyed***");
if(mRenderCallback!=null){
mRenderCallback.onSurfaceDestroy(new InternalRenderHolder(holder));
}
}
}
}
RelationAssist的部分代码
//...
mRender.setRenderCallback(mRenderCallback);
//...
private IRender.IRenderCallback mRenderCallback =new IRender.IRenderCallback() {
@Override
public void onSurfaceCreated(IRender.IRenderHolder renderHolder,
int width, int height) {
PLog.d(TAG,"onSurfaceCreated : width = " + width + ", height = " + height);
//当 surface 创建的时候关联播放器
mRenderHolder = renderHolder;
bindRenderHolder(mRenderHolder);
}
@Override
public void onSurfaceChanged(IRender.IRenderHolder renderHolder,
int format, int width, int height) {
//not handle some...
}
@Override
public void onSurfaceDestroy(IRender.IRenderHolder renderHolder) {
PLog.d(TAG,"onSurfaceDestroy...");
//on surface destroy detach player
mRenderHolder = null;
}
};
private void bindRenderHolder(IRender.IRenderHolder renderHolder){
if(renderHolder!=null)
renderHolder.bindPlayer(mPlayer);
}
第一次开始播放的时候RenderSurfaceView的SurfaceHolder的生命周期
com.kk.taurus.avplayer D/RenderSurfaceView: <---surfaceCreated---->
com.kk.taurus.avplayer D/RenderSurfaceView: surfaceChanged : width = 1080 height = 600
com.kk.taurus.avplayer D/RenderSurfaceView: surfaceChanged : width = 960 height = 540
当我们更换视频播放的父级布局的时候RenderSurfaceView的SurfaceHolder的生命周期
com.kk.taurus.avplayer D/RenderSurfaceView: ***surfaceDestroyed***
com.kk.taurus.avplayer D/RenderSurfaceView: <---surfaceCreated---->
com.kk.taurus.avplayer D/RenderSurfaceView: surfaceChanged : width = 960 height = 540
SurfaceHolder会Destroyed,然后会重新创建,而这时,RelationAssit只是把播放器和重新创建好的SurfaceHolder关联而已。
@Override
public void bindPlayer(IPlayer player) {
if(player!=null && mSurfaceHolder.get()!=null){
//设置播放器的SurfaceHolder用来显示画面
player.setDisplay(mSurfaceHolder.get());
}
}
总结,其实对于这种无缝的续播,原理很简单。就是不同的渲染视图使用同一个解码实例即可。可以简单比作一个MediaPlayer去不断设置不同的surface呈现播放。有兴趣可以自己试着实现一把,不使用PlayerBase来完成这个功能。
参考链接: