|
|
|
@ -1,8 +1,8 @@ |
|
|
|
package qianmu.container.view; |
|
|
|
|
|
|
|
import android.content.Context; |
|
|
|
import android.graphics.Bitmap; |
|
|
|
import android.graphics.Color; |
|
|
|
import android.graphics.PixelFormat; |
|
|
|
import android.graphics.drawable.Drawable; |
|
|
|
import android.util.AttributeSet; |
|
|
|
import android.util.Log; |
|
|
|
@ -11,7 +11,6 @@ import android.view.TextureView; |
|
|
|
import android.view.View; |
|
|
|
import android.view.ViewGroup; |
|
|
|
import android.widget.FrameLayout; |
|
|
|
import android.widget.VideoView; |
|
|
|
|
|
|
|
import androidx.annotation.NonNull; |
|
|
|
|
|
|
|
@ -22,42 +21,24 @@ import com.google.android.exoplayer2.PlaybackParameters; |
|
|
|
import com.google.android.exoplayer2.Player; |
|
|
|
import com.google.android.exoplayer2.SimpleExoPlayer; |
|
|
|
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; |
|
|
|
import com.google.android.exoplayer2.ui.PlayerView; |
|
|
|
import com.google.android.exoplayer2.ui.StyledPlayerView; |
|
|
|
|
|
|
|
/** |
|
|
|
* Created by Android Studio. |
|
|
|
* User: linzhibin |
|
|
|
* Date: 2021/5/19 |
|
|
|
* Time: 13:59 |
|
|
|
*/ |
|
|
|
public class CustomerVideoView extends FrameLayout { |
|
|
|
|
|
|
|
private StyledPlayerView playerView; |
|
|
|
private ExoPlayer player; |
|
|
|
private TextureView mTextureView; // 我们自己管理 TextureView
|
|
|
|
private OnCompletionListener onCompletionListener; |
|
|
|
private OnErrorListener onErrorListener; |
|
|
|
private OnPreparedListener onPreparedListener; |
|
|
|
private OnInfoListener onInfoListener; |
|
|
|
private Boolean isFirstFame = false; |
|
|
|
|
|
|
|
// 监听器接口,保持与 VideoView 兼容
|
|
|
|
public interface OnCompletionListener { |
|
|
|
void onCompletion(); |
|
|
|
} |
|
|
|
|
|
|
|
public interface OnErrorListener { |
|
|
|
boolean onError(); |
|
|
|
} |
|
|
|
|
|
|
|
public interface OnPreparedListener { |
|
|
|
void onPrepared(); |
|
|
|
} |
|
|
|
|
|
|
|
public interface OnInfoListener { |
|
|
|
void onInfo(); |
|
|
|
} |
|
|
|
|
|
|
|
// 接口保持不变
|
|
|
|
public interface OnCompletionListener { void onCompletion(); } |
|
|
|
public interface OnErrorListener { boolean onError(); } |
|
|
|
public interface OnPreparedListener { void onPrepared(); } |
|
|
|
public interface OnInfoListener { void onInfo(); } |
|
|
|
|
|
|
|
public CustomerVideoView(Context context) { |
|
|
|
super(context); |
|
|
|
@ -75,121 +56,103 @@ public class CustomerVideoView extends FrameLayout { |
|
|
|
} |
|
|
|
|
|
|
|
private void init(Context context) { |
|
|
|
// 创建 StyledPlayerView
|
|
|
|
// 1. 创建 StyledPlayerView
|
|
|
|
playerView = new StyledPlayerView(context); |
|
|
|
addView(playerView); |
|
|
|
// 初始化 ExoPlayer
|
|
|
|
// 2. 初始化播放器
|
|
|
|
initializePlayer(); |
|
|
|
// 设置背景透明
|
|
|
|
|
|
|
|
// 透明设置
|
|
|
|
playerView.setBackgroundColor(Color.TRANSPARENT); |
|
|
|
playerView.setShutterBackgroundColor(Color.TRANSPARENT); |
|
|
|
// 3. 设置使用透明背景
|
|
|
|
playerView.setUseArtwork(false); // 如果不需要 artwork
|
|
|
|
playerView.setDefaultArtwork(null); // 清除默认 artwork
|
|
|
|
// 4. 设置 SurfaceView 透明
|
|
|
|
View videoSurfaceView = playerView.getVideoSurfaceView(); |
|
|
|
if (videoSurfaceView != null) { |
|
|
|
videoSurfaceView.setBackgroundColor(Color.TRANSPARENT); |
|
|
|
// 如果使用 SurfaceView,设置格式支持透明
|
|
|
|
if (videoSurfaceView instanceof SurfaceView) { |
|
|
|
Log.e("View: ","SurfaceView"); |
|
|
|
SurfaceView surfaceView = (SurfaceView) videoSurfaceView; |
|
|
|
replaceWithTextureView(playerView, (SurfaceView) surfaceView); |
|
|
|
}else if (videoSurfaceView instanceof TextureView) { |
|
|
|
// TextureView 默认支持透明
|
|
|
|
videoSurfaceView.setBackgroundColor(Color.TRANSPARENT); |
|
|
|
} |
|
|
|
} |
|
|
|
playerView.setUseArtwork(false); |
|
|
|
playerView.setDefaultArtwork(null); |
|
|
|
|
|
|
|
replaceSurfaceWithTextureView(); |
|
|
|
} |
|
|
|
|
|
|
|
private void replaceWithTextureView(StyledPlayerView playerView, SurfaceView oldSurface) { |
|
|
|
ViewGroup parent = (ViewGroup) oldSurface.getParent(); |
|
|
|
if (parent != null) { |
|
|
|
// 创建新的 TextureView
|
|
|
|
TextureView textureView = new TextureView(getContext()); |
|
|
|
textureView.setLayoutParams(oldSurface.getLayoutParams()); |
|
|
|
/** |
|
|
|
* 安全替换 SurfaceView 为 TextureView |
|
|
|
*/ |
|
|
|
private void replaceSurfaceWithTextureView() { |
|
|
|
if (playerView == null) return; |
|
|
|
|
|
|
|
// 延迟获取,确保 View 已创建
|
|
|
|
playerView.post(() -> { |
|
|
|
View videoView = playerView.getVideoSurfaceView(); |
|
|
|
if (!(videoView instanceof SurfaceView)) return; |
|
|
|
|
|
|
|
SurfaceView oldSurface = (SurfaceView) videoView; |
|
|
|
ViewGroup parent = (ViewGroup) oldSurface.getParent(); |
|
|
|
if (parent == null) return; |
|
|
|
|
|
|
|
// 创建 TextureView
|
|
|
|
mTextureView = new TextureView(getContext()); |
|
|
|
mTextureView.setLayoutParams(oldSurface.getLayoutParams()); |
|
|
|
|
|
|
|
// 替换
|
|
|
|
int index = parent.indexOfChild(oldSurface); |
|
|
|
parent.removeView(oldSurface); |
|
|
|
parent.addView(textureView, index); |
|
|
|
parent.addView(mTextureView, index); |
|
|
|
|
|
|
|
// 设置到播放器
|
|
|
|
if (playerView.getPlayer() != null) { |
|
|
|
playerView.getPlayer().setVideoTextureView(textureView); |
|
|
|
// 绑定到播放器
|
|
|
|
if (player != null) { |
|
|
|
player.setVideoTextureView(mTextureView); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
private void initializePlayer() { |
|
|
|
// 创建 ExoPlayer 实例
|
|
|
|
player = new SimpleExoPlayer.Builder(getContext()) |
|
|
|
.setTrackSelector(new DefaultTrackSelector(getContext())) |
|
|
|
.build(); |
|
|
|
player.setPlayWhenReady(false); |
|
|
|
// 设置播放器到 PlayerView
|
|
|
|
playerView.setPlayer(player); |
|
|
|
setupPlayerListeners(); |
|
|
|
// 默认设置(可以根据需要调整)
|
|
|
|
playerView.setUseController(false); |
|
|
|
// 默认设置 Surface 在底部
|
|
|
|
setZOrderOnTop(false); |
|
|
|
setZOrderMediaOverlay(false); |
|
|
|
} |
|
|
|
|
|
|
|
private void setupPlayerListeners() { |
|
|
|
if (player == null) return; |
|
|
|
// 播放完成监听
|
|
|
|
player.addListener(new Player.Listener() { |
|
|
|
@Override |
|
|
|
public void onPlaybackStateChanged(int playbackState) { |
|
|
|
if (playbackState == Player.STATE_ENDED) { |
|
|
|
if (onCompletionListener != null) { |
|
|
|
if (onCompletionListener != null) |
|
|
|
onCompletionListener.onCompletion(); |
|
|
|
} |
|
|
|
isFirstFame = false; |
|
|
|
} |
|
|
|
if(playbackState == Player.STATE_READY){ |
|
|
|
Log.e("loadVideo: ","STATE_READY"); |
|
|
|
// 视频准备就绪
|
|
|
|
if (onPreparedListener != null) { |
|
|
|
if (playbackState == Player.STATE_READY) { |
|
|
|
if (onPreparedListener != null) |
|
|
|
onPreparedListener.onPrepared(); |
|
|
|
} |
|
|
|
setVisibility(VISIBLE); |
|
|
|
if(playerView != null && !isFirstFame){ |
|
|
|
if (playerView != null && !isFirstFame) |
|
|
|
playerView.setAlpha(0f); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void onPlayerError(@NonNull PlaybackException error) { |
|
|
|
if (onErrorListener != null) { |
|
|
|
if (onErrorListener != null) |
|
|
|
onErrorListener.onError(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void onRenderedFirstFrame() { |
|
|
|
Log.e("loadVideo: ","FirstFrame"); |
|
|
|
if (onInfoListener != null) { |
|
|
|
if (onInfoListener != null) |
|
|
|
onInfoListener.onInfo(); |
|
|
|
} |
|
|
|
isFirstFame = true; |
|
|
|
if(playerView != null){ |
|
|
|
if (playerView != null) |
|
|
|
playerView.setAlpha(1f); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { |
|
|
|
// 保持原有的测量逻辑
|
|
|
|
int width = getDefaultSize(0, widthMeasureSpec); |
|
|
|
int height = getDefaultSize(0, heightMeasureSpec); |
|
|
|
setMeasuredDimension(width, height); |
|
|
|
|
|
|
|
// 让 StyledPlayerView 填满整个父布局
|
|
|
|
if (playerView != null) { |
|
|
|
playerView.measure( |
|
|
|
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), |
|
|
|
@ -201,151 +164,71 @@ public class CustomerVideoView extends FrameLayout { |
|
|
|
@Override |
|
|
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) { |
|
|
|
super.onLayout(changed, left, top, right, bottom); |
|
|
|
// 布局 StyledPlayerView 使其填满整个控件
|
|
|
|
if (playerView != null) { |
|
|
|
if (playerView != null) |
|
|
|
playerView.layout(0, 0, getWidth(), getHeight()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// ==================== 实现 VideoView 的常用方法 ====================
|
|
|
|
|
|
|
|
// 1. setBackground - 设置背景
|
|
|
|
@Override |
|
|
|
public void setBackground(Drawable background) { |
|
|
|
super.setBackground(background); |
|
|
|
// 如果需要设置 PlayerView 的背景
|
|
|
|
if (playerView != null) { |
|
|
|
playerView.setVisibility(GONE); |
|
|
|
} |
|
|
|
if (playerView != null) playerView.setVisibility(GONE); |
|
|
|
} |
|
|
|
|
|
|
|
public void clearBackground(){ |
|
|
|
public void clearBackground() { |
|
|
|
super.setBackground(null); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void setBackgroundColor(int color) { |
|
|
|
super.setBackgroundColor(color); |
|
|
|
if (color != Color.TRANSPARENT && playerView != null) { |
|
|
|
if (color != Color.TRANSPARENT && playerView != null) |
|
|
|
playerView.setVisibility(GONE); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
public void setBackgroundResource(int resid) { |
|
|
|
super.setBackgroundResource(resid); |
|
|
|
if (playerView != null) { |
|
|
|
if (playerView != null) |
|
|
|
playerView.setVisibility(INVISIBLE); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 2. setZOrderOnTop - 设置 Surface 是否在最顶层
|
|
|
|
public void setZOrderOnTop(boolean onTop) { |
|
|
|
if (playerView != null) { |
|
|
|
// 获取 PlayerView 内部的 SurfaceView
|
|
|
|
View videoSurfaceView = playerView.getVideoSurfaceView(); |
|
|
|
if (videoSurfaceView instanceof SurfaceView) { |
|
|
|
SurfaceView surfaceView = (SurfaceView) videoSurfaceView; |
|
|
|
surfaceView.setZOrderOnTop(onTop); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
public void setZOrderOnTop(boolean onTop) {} |
|
|
|
public void setZOrderMediaOverlay(boolean isMediaOverlay) {} |
|
|
|
|
|
|
|
// 3. setZOrderMediaOverlay - 设置 Surface 作为媒体叠加层
|
|
|
|
public void setZOrderMediaOverlay(boolean isMediaOverlay) { |
|
|
|
if (playerView != null) { |
|
|
|
View videoSurfaceView = playerView.getVideoSurfaceView(); |
|
|
|
if (videoSurfaceView instanceof SurfaceView) { |
|
|
|
SurfaceView surfaceView = (SurfaceView) videoSurfaceView; |
|
|
|
surfaceView.setZOrderMediaOverlay(isMediaOverlay); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 4. setVisibility - 设置可见性
|
|
|
|
@Override |
|
|
|
public void setVisibility(int visibility) { |
|
|
|
super.setVisibility(visibility); |
|
|
|
if (playerView != null) { |
|
|
|
if (playerView != null) |
|
|
|
playerView.setVisibility(visibility); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 5. 视频控制相关方法
|
|
|
|
public void setVideoPath(String path) { |
|
|
|
if (player != null && path != null) { |
|
|
|
try { |
|
|
|
MediaItem mediaItem = MediaItem.fromUri(path); |
|
|
|
player.setMediaItem(mediaItem); |
|
|
|
player.prepare(); |
|
|
|
player.setPlaybackParameters(new PlaybackParameters(1.0f)); |
|
|
|
} catch (Exception e) {} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void start() { |
|
|
|
if (player != null) { |
|
|
|
player.play(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public void pause() { |
|
|
|
if (player != null) { |
|
|
|
player.pause(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public void stopPlayback() { |
|
|
|
if (player != null) { |
|
|
|
player.stop(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public void seekTo(int position) { |
|
|
|
if (player != null) { |
|
|
|
player.seekTo(position); |
|
|
|
MediaItem mediaItem = MediaItem.fromUri(path); |
|
|
|
player.setMediaItem(mediaItem); |
|
|
|
player.prepare(); |
|
|
|
player.setPlaybackParameters(new PlaybackParameters(1.0f)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public boolean isPlaying() { |
|
|
|
return player != null && player.isPlaying(); |
|
|
|
} |
|
|
|
|
|
|
|
// 6. 监听器设置
|
|
|
|
public void setOnCompletionListener(OnCompletionListener listener) { |
|
|
|
this.onCompletionListener = listener; |
|
|
|
} |
|
|
|
|
|
|
|
public void setOnPreparedListener(OnPreparedListener listener) { |
|
|
|
this.onPreparedListener = listener; |
|
|
|
} |
|
|
|
public void start() { if (player != null) player.play(); } |
|
|
|
public void pause() { if (player != null) player.pause(); } |
|
|
|
public void stopPlayback() { if (player != null) player.stop(); } |
|
|
|
public void seekTo(int position) { if (player != null) player.seekTo(position); } |
|
|
|
public boolean isPlaying() { return player != null && player.isPlaying(); } |
|
|
|
|
|
|
|
public void setOnInfoListener(OnInfoListener listener) { |
|
|
|
this.onInfoListener = listener; |
|
|
|
} |
|
|
|
|
|
|
|
public void setOnErrorListener(OnErrorListener listener) { |
|
|
|
this.onErrorListener = listener; |
|
|
|
} |
|
|
|
|
|
|
|
// 可选:添加与 ExoPlayer 兼容的错误监听器
|
|
|
|
public void setOnExoPlayerErrorListener(Player.Listener errorListener) { |
|
|
|
if (player != null && errorListener != null) { |
|
|
|
player.addListener(errorListener); |
|
|
|
} |
|
|
|
} |
|
|
|
public void setOnCompletionListener(OnCompletionListener listener) { this.onCompletionListener = listener; } |
|
|
|
public void setOnPreparedListener(OnPreparedListener listener) { this.onPreparedListener = listener; } |
|
|
|
public void setOnInfoListener(OnInfoListener listener) { this.onInfoListener = listener; } |
|
|
|
public void setOnErrorListener(OnErrorListener listener) { this.onErrorListener = listener; } |
|
|
|
|
|
|
|
public void release() { |
|
|
|
if (player != null) { |
|
|
|
player.setVideoTextureView(null); |
|
|
|
player.release(); |
|
|
|
player = null; |
|
|
|
} |
|
|
|
if (playerView != null) { |
|
|
|
playerView.setPlayer(null); |
|
|
|
} |
|
|
|
onCompletionListener = null; |
|
|
|
onErrorListener = null; |
|
|
|
if (playerView != null) playerView.setPlayer(null); |
|
|
|
} |
|
|
|
|
|
|
|
@Override |
|
|
|
@ -354,7 +237,6 @@ public class CustomerVideoView extends FrameLayout { |
|
|
|
release(); |
|
|
|
} |
|
|
|
|
|
|
|
// 重置播放器
|
|
|
|
public void reset() { |
|
|
|
if (player != null) { |
|
|
|
player.stop(); |
|
|
|
@ -362,12 +244,7 @@ public class CustomerVideoView extends FrameLayout { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 设置音量
|
|
|
|
public void setVolume(float volume) { |
|
|
|
if (player != null) { |
|
|
|
player.setVolume(volume); |
|
|
|
} |
|
|
|
if (player != null) player.setVolume(volume); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |