pdlivexp/live/src/main/java/com/yunbao/live/utils/LiveExoPlayerManager.java

383 lines
14 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.yunbao.live.utils;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import androidx.annotation.NonNull;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.MediaItem;
import com.google.android.exoplayer2.PlaybackException;
import com.google.android.exoplayer2.Player;
import com.google.android.exoplayer2.analytics.AnalyticsListener;
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout;
import com.google.android.exoplayer2.ui.StyledPlayerView;
import com.google.android.exoplayer2.video.VideoSize;
/**
* 直播间播放器管理器
* 通过预加载子播放器来实现无缝切换分辨率功能
*/
public class LiveExoPlayerManager {
private final int MODEL_PLAY1 = 0;//当前主播放器
private final int MODEL_PLAY2 = 1;//当前子播放器
private final ExoPlayer player1;
private final ExoPlayer player2;
private StyledPlayerView mainView;//渲染视图
private int status = MODEL_PLAY1;
private Player.Listener listener;
private boolean isSwitchUrl = false;//是否为主动切换播放器
private final String TAG = "播放器";
private int playBufferIndex = 0;//卡顿计数器
private final Handler handler;
private static double log_buffer_time = 0, log_buffer_max_time;
private String url = "";
public LiveExoPlayerManager(Context mContext) {
DefaultLoadControl control = new DefaultLoadControl.Builder()
.setPrioritizeTimeOverSizeThresholds(false)
.setBackBuffer(10_000, true)
.setBufferDurationsMs(500,
5_000,
150,
200)
.build();
player1 = new ExoPlayer.Builder(mContext).setLoadControl(control).build();
player2 = new ExoPlayer.Builder(mContext).setLoadControl(control).build();
player1.setVideoScalingMode(C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
player2.setVideoScalingMode(C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
handler = new Handler(Looper.getMainLooper());
setListener();
setAnalyticsListener();
}
public void setListener(Player.Listener listener) {
this.listener = listener;
}
public void setMainView(StyledPlayerView mainView) {
this.mainView = mainView;
this.mainView.setKeepContentOnPlayerReset(true);
}
public void setViewResizeMode(boolean isPhone) {
Log.i(TAG, "setViewResizeMode: " + isPhone);
mainView.setResizeMode(isPhone ? AspectRatioFrameLayout.RESIZE_MODE_ZOOM : AspectRatioFrameLayout.RESIZE_MODE_FIT);
mainView.requestLayout();
}
public boolean isViewResizeModeToPhone() {
return mainView.getResizeMode() == AspectRatioFrameLayout.RESIZE_MODE_ZOOM;
}
private void setAnalyticsListener() {
player1.addAnalyticsListener(new AnalyticsListener() {
@Override
public void onPlaybackStateChanged(
@NonNull EventTime eventTime, int state) {
Log.d(TAG, "onPlaybackStateChanged(1) called with: eventTime = [" + eventTime + "], state = [" + state + "]");
}
@Override
public void onDroppedVideoFrames(
@NonNull EventTime eventTime, int droppedFrames, long elapsedMs) {
Log.d(TAG, "onDroppedVideoFrames(1) called with: eventTime = [" + eventTime + "], droppedFrames = [" + droppedFrames + "], elapsedMs = [" + elapsedMs + "]");
}
});
player2.addAnalyticsListener(new AnalyticsListener() {
@Override
public void onPlaybackStateChanged(
@NonNull EventTime eventTime, int state) {
Log.d(TAG, "onPlaybackStateChanged(2) called with: eventTime = [" + eventTime.totalBufferedDurationMs + "], state = [" + state + "]");
}
@Override
public void onDroppedVideoFrames(
@NonNull EventTime eventTime, int droppedFrames, long elapsedMs) {
Log.d(TAG, "onDroppedVideoFrames(2) called with: eventTime = [" + eventTime + "], droppedFrames = [" + droppedFrames + "], elapsedMs = [" + elapsedMs + "]");
}
});
}
/**
* 延迟1秒还未恢复播放则认为卡顿了可以切分辨率了
*/
private final Runnable buffRunnable = new Runnable() {
@Override
public void run() {
playBufferIndex = 0;
listener.onPlaybackStateChanged(Player.STATE_BUFFERING);
replay();
}
};
private void setListener() {
player1.addListener(new Player.Listener() {
@Override
public void onPlaybackStateChanged(int playbackState) {
Player.Listener.super.onPlaybackStateChanged(playbackState);
Log.i(TAG, "onPlaybackStateChanged 1: " + playbackState);
if (playbackState == Player.STATE_READY && !player1.isPlaying()) {
player2.stop();
player2.clearVideoSurface();
player1.play();
Log.i(TAG, "切换播放器1");
handler.removeCallbacks(buffRunnable);
playBufferIndex = 0;
} else if (playbackState == Player.STATE_BUFFERING && status == MODEL_PLAY1 && !isSwitchUrl) {
if (listener != null) {
if (playBufferIndex++ == 0) {
handler.postDelayed(buffRunnable, 2000);
}
}
}
}
@Override
public void onIsPlayingChanged(boolean isPlaying) {
Player.Listener.super.onIsPlayingChanged(isPlaying);
if (isPlaying) {
Log.i(TAG, "onIsPlayingChanged1: 播放了");
//player1.setVideoSurfaceView(mainView);
if (log_buffer_time != -1) {
double tmp = (System.currentTimeMillis() - log_buffer_time) / 1000;
if (tmp > log_buffer_max_time) {
log_buffer_max_time = tmp;
}
//ToastUtil.show(String.format(Locale.CHINA, "从加载到播放 = %.3f,最大耗时 = %.3f", tmp, log_buffer_max_time));
log_buffer_time = -1;
}
mainView.setPlayer(player1);
status = MODEL_PLAY1;
isSwitchUrl = false;
handler.removeCallbacks(buffRunnable);
if (getNextPlayer().isPlaying()) {
getNextPlayer().stop();
}
if (listener != null) {
listener.onIsPlayingChanged(true);
}
}
}
@Override
public void onVideoSizeChanged(@NonNull VideoSize videoSize) {
Player.Listener.super.onVideoSizeChanged(videoSize);
setViewResizeMode(videoSize.height > videoSize.width);
Log.i(TAG, "onVideoSizeChanged: width = " + videoSize.width + " height = " + videoSize.height);
if (listener != null) {
listener.onVideoSizeChanged(videoSize);
}
}
@Override
public void onIsLoadingChanged(boolean isLoading) {
Player.Listener.super.onIsLoadingChanged(isLoading);
Log.i(TAG, "onIsLoadingChanged: 1 " + isLoading);
}
@Override
public void onPlayerError(@NonNull PlaybackException error) {
Player.Listener.super.onPlayerError(error);
error.printStackTrace();
replay();
}
});
player2.addListener(new Player.Listener() {
@Override
public void onPlaybackStateChanged(int playbackState) {
Player.Listener.super.onPlaybackStateChanged(playbackState);
Log.i(TAG, "onPlaybackStateChanged 2: " + playbackState);
if (playbackState == Player.STATE_READY && !player2.isPlaying()) {
player1.stop();
player1.clearVideoSurface();
player2.play();
Log.i(TAG, "切换播放器2 " + player2.isPlaying());
handler.removeCallbacks(buffRunnable);
playBufferIndex = 0;
} else if (playbackState == Player.STATE_BUFFERING && status == MODEL_PLAY2 && !isSwitchUrl) {
if (listener != null) {
if (playBufferIndex++ == 0) {
handler.postDelayed(buffRunnable, 2000);
}
}
}
}
@Override
public void onIsPlayingChanged(boolean isPlaying) {
Player.Listener.super.onIsPlayingChanged(isPlaying);
if (isPlaying) {
Log.i(TAG, "onIsPlayingChanged2: 播放了");
//player2.setVideoSurfaceView(mainView);
mainView.setPlayer(player2);
double tmp = (System.currentTimeMillis() - log_buffer_time) / 1000;
if (tmp > log_buffer_max_time) {
log_buffer_max_time = tmp;
}
//ToastUtil.show(String.format(Locale.CHINA, "从加载到播放 = %.3f,最大耗时 = %.3f", tmp, log_buffer_max_time));
log_buffer_time = -1;
status = MODEL_PLAY2;
if (getNextPlayer().isPlaying()) {
getNextPlayer().stop();
}
handler.removeCallbacks(buffRunnable);
isSwitchUrl = false;
if (listener != null) {
listener.onIsPlayingChanged(true);
}
}
}
@Override
public void onVideoSizeChanged(@NonNull VideoSize videoSize) {
Player.Listener.super.onVideoSizeChanged(videoSize);
setViewResizeMode(videoSize.height > videoSize.width);
if (listener != null) {
listener.onVideoSizeChanged(videoSize);
}
}
@Override
public void onIsLoadingChanged(boolean isLoading) {
Player.Listener.super.onIsLoadingChanged(isLoading);
Log.i(TAG, "onIsLoadingChanged: 2 " + isLoading);
}
@Override
public void onPlayerError(@NonNull PlaybackException error) {
Player.Listener.super.onPlayerError(error);
error.printStackTrace();
replay();
}
});
}
/**
* 开始播放
*
* @param url 地址
*/
public void startUrl(String url) {
if (url != null && url.equals(this.url)) return;
Log.i(TAG, "startUrl: " + url + " > " + mainView.getResizeMode());
handler.removeCallbacks(buffRunnable);
this.url = url;
isSwitchUrl = true;
playBufferIndex = 0;
log_buffer_time = System.currentTimeMillis();
//getNowPlayer().setVideoSurfaceView(mainView);
mainView.setKeepContentOnPlayerReset(false);
mainView.setPlayer(getNowPlayer());
mainView.setKeepContentOnPlayerReset(true);
getNowPlayer().setMediaItem(createMediaItem(url));
getNowPlayer().prepare();
getNowPlayer().play();
handler.postDelayed(buffRunnable, 10000);
}
/**
* 无缝切换
*
* @param url 地址
*/
public void switchUrl(String url) {
if (url != null && url.equals(this.url)) return;
Log.i(TAG, "switchUrl: " + url + " src : " + this.url);
this.url = url;
playBufferIndex = 0;
isSwitchUrl = true;
log_buffer_time = System.currentTimeMillis();
mainView.setKeepContentOnPlayerReset(true);
getNextPlayer().setMediaItem(createMediaItem(url));
getNextPlayer().prepare();
}
private MediaItem createMediaItem(String url) {
return MediaItem.fromUri(url);
}
/**
* 获取当前播放器
*/
public ExoPlayer getNowPlayer() {
return status == MODEL_PLAY1 ? player1 : player2;
}
/**
* 获取下一个播放器。
*/
private ExoPlayer getNextPlayer() {
return status == MODEL_PLAY1 ? player2 : player1;
}
/**
* 是否正在播放
*/
public boolean isPlaying() {
return getNowPlayer().isPlaying();
}
/**
* 停止播放
*/
public void stop() {
getNowPlayer().stop();
clearUrl();
}
/**
* 开始播放
*/
public void play() {
getNowPlayer().play();
}
public void replay() {
Log.i(TAG, "replay: 重载播放");
getNowPlayer().stop();
getNextPlayer().stop();
String tmp = url;
url = null;
startUrl(tmp);
}
public void clearFrame() {
mainView.setKeepContentOnPlayerReset(false);
if (mainView.getVideoSurfaceView() != null) {
mainView.setPlayer(null);
}
}
/**
* 释放播放器
*/
public void release() {
Log.i(TAG, "release: 释放播放器");
player1.release();
player2.release();
handler.removeCallbacks(buffRunnable);
}
public void clearUrl() {
url = "";
handler.removeCallbacks(buffRunnable);
}
public String getUrl() {
if (url == null) {
url = "";
}
return url;
}
}