private PLVideoTextureView mVideoView; //播放器
private MediaController mMediaController = null; //控制器
private int mIsLiveStreaming = 1;
private boolean mIsActivityPaused = true;
private boolean llSuccess = false; //拉流成功
/* 七牛设置 str -------------------------------------------------------*/
/**
* 设置基本七牛基本信息-->开始拉流
*/
public void startLL() {
mIsLiveStreaming = 1; // 1 = 直播 / 0 = 点播
int codec = AVOptions.MEDIA_CODEC_AUTO; //解码方式 sw = 软解 / hw = 硬解 / auto = 自动
setOptions(codec);
// You can mirror the display 预览画面反转
// mVideoView.setMirror(true);
// You can also use a custom `MediaController` widget
mMediaController = new MediaController(this, false, mIsLiveStreaming == 1);
mVideoView.setMediaController(mMediaController);
//mVideoView.setDisplayOrientation(PLVideoTextureView.ASPECT_RATIO_FIT_PARENT); //default-->ASPECT_RATIO_FIT_PARENT 画面旋转
mVideoView.setDisplayAspectRatio(PLVideoView.ASPECT_RATIO_PAVED_PARENT); //画面预览模式
mVideoView.setOnInfoListener(new PLMediaPlayer.OnInfoListener() { //播放器状态监听
@Override
public boolean onInfo(PLMediaPlayer plMediaPlayer, int i, int i1) {
switch (i) {
case PLMediaPlayer.MEDIA_INFO_UNKNOWN:
logger("未知消息");
break;
case PLMediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START: //加载成功,正在播放
logger("第一帧视频已成功渲染");
llSuccess = true;
break;
case PLMediaPlayer.MEDIA_INFO_BUFFERING_START:
logger("开始缓冲");
break;
case PLMediaPlayer.MEDIA_INFO_BUFFERING_END:
logger("停止缓冲");
break;
case PLMediaPlayer.MEDIA_INFO_VIDEO_ROTATION_CHANGED:
logger("获取到视频的播放角度");
break;
case PLMediaPlayer.MEDIA_INFO_AUDIO_RENDERING_START:
logger("第一帧音频已成功播放");
break;
}
return false;
}
});
mVideoView.setOnCompletionListener(new PLMediaPlayer.OnCompletionListener() { //直播结束监听
@Override
public void onCompletion(PLMediaPlayer plMediaPlayer) {
}
});
mVideoView.setOnErrorListener(new PLMediaPlayer.OnErrorListener() { //直播错误监听
@Override
public boolean onError(PLMediaPlayer plMediaPlayer, int i) {
boolean isNeedReconnect = false; //是否需要重新连接
boolean customReturn = false;
switch (i) {
case PLMediaPlayer.ERROR_CODE_INVALID_URI:
logger("Invalid URL !无效的 URL");
break;
case PLMediaPlayer.ERROR_CODE_404_NOT_FOUND:
logger("404 resource not found !播放资源不存在");
break;
case PLMediaPlayer.ERROR_CODE_CONNECTION_REFUSED:
logger("Connection refused !服务器拒绝连接");
break;
case PLMediaPlayer.ERROR_CODE_CONNECTION_TIMEOUT:
logger("Connection timeout !连接超时");
isNeedReconnect = true;
break;
case PLMediaPlayer.ERROR_CODE_EMPTY_PLAYLIST:
logger("Empty playlist !空的播放列表");
break;
case PLMediaPlayer.ERROR_CODE_STREAM_DISCONNECTED:
logger("Stream disconnected !与服务器连接断开");
isNeedReconnect = true;
break;
case PLMediaPlayer.ERROR_CODE_IO_ERROR:
logger("Network IO Error !网络异常");
isNeedReconnect = true;
break;
case PLMediaPlayer.ERROR_CODE_UNAUTHORIZED:
logger("Unauthorized Error !");
break;
case PLMediaPlayer.ERROR_CODE_PREPARE_TIMEOUT:
logger("Prepare timeout !未授权,播放一个禁播的流");
// TODO: 2017/2/16 如果主播正常断开连接,第二次及之后所有的提示将会提示这个提示
liveEnd2();
customReturn = true;
//isNeedReconnect = true;
break;
case PLMediaPlayer.ERROR_CODE_READ_FRAME_TIMEOUT:
// TODO: 2017/2/16 如果主播正常断开连接,第一次提示这个提示
logger("Read frame timeout !读取数据超时");
isNeedReconnect = true;
break;
case PLMediaPlayer.ERROR_CODE_HW_DECODE_FAILURE:
setOptions(AVOptions.MEDIA_CODEC_SW_DECODE);
isNeedReconnect = true;
break;
case PLMediaPlayer.MEDIA_ERROR_UNKNOWN:
logger("未知错误");
break;
default:
logger("unknown error !未知错误");
break;
}
// Todo pls handle the error status here, reconnect or call finish()
if (isNeedReconnect) {
sendReconnectMessage();
} else {
// TODO: 2017/3/19 直播错误,请手动重连...
if (!customReturn)
liveEnd();
}
// Return true means the error has been handled
// If return false, then `onCompletion` will be called
//如果返回true-->错误已被处理 / 返回false-->setOnCompletionListener上交给国家
return true;
}
});
mVideoView.setVideoPath(mLLAddress);
mVideoView.start();
}
/**
* 设置播放器参数
*/
private void setOptions(int codecType) {
AVOptions options = new AVOptions();
// the unit of timeout is ms
options.setInteger(AVOptions.KEY_PREPARE_TIMEOUT, 10 * 1000);
options.setInteger(AVOptions.KEY_GET_AV_FRAME_TIMEOUT, 10 * 1000);
options.setInteger(AVOptions.KEY_PROBESIZE, 128 * 1024);
// Some optimization with buffering mechanism when be set to 1
options.setInteger(AVOptions.KEY_LIVE_STREAMING, mIsLiveStreaming);
if (mIsLiveStreaming == 1) {
options.setInteger(AVOptions.KEY_DELAY_OPTIMIZATION, 1);
}
// 1 -> hw codec enable, 0 -> disable [recommended]
options.setInteger(AVOptions.KEY_MEDIACODEC, codecType);
// whether start play automatically after prepared, default value is 1
options.setInteger(AVOptions.KEY_START_ON_PREPARED, 0);
mVideoView.setAVOptions(options);
}
/**
* 发送重连消息
*/
private void sendReconnectMessage() {
showToast(getStr(R.string.reconnection));
mHandler.removeCallbacksAndMessages(null);
mHandler.sendMessageDelayed(mHandler.obtainMessage(MESSAGE_ID_RECONNECTING), 500);
}
/**
* 重连 同时对当前流是否可用做出判断
*/
private static final int MESSAGE_ID_RECONNECTING = 0x01;
protected Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
if (msg.what != MESSAGE_ID_RECONNECTING) {
return;
}
if (mIsActivityPaused) { // TODO: 2017/2/16 同时可以增加后台确认当前主播是否在直播 (暂不使用)
return;
}
if (!NetworkUtils.isAvailableByPing()) {
sendReconnectMessage();
return;
}
mVideoView.setVideoPath(mLLAddress);
mVideoView.start();
}
};
/* 七牛设置 end ---------------------------------*/
@Override
protected void onPause() {
super.onPause();
mVideoView.pause();
mIsActivityPaused = true;
}
@Override
protected void onResume() {
super.onResume();
mIsActivityPaused = false;
mVideoView.start();
}
@Override
protected void onDestroy() {
super.onDestroy();
mVideoView.stopPlayback();
}