package com.sdxw.floatbottle.utils.record;
import android.Manifest;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.view.Gravity;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ProgressBar;
import androidx.annotation.NonNull;
import com.sdxw.floatbottle.utils.L;
import java.io.FileDescriptor;
import java.io.IOException;
/**
-
@创建日志 2021-2-7 13:30
-
@描述 视频播放器 TextureView+MediaPlayer
-
@备注 播放网络视频需要添加权限 Manifest.permission.INTERNET
*/
public class VideoPlayer implements TextureView.SurfaceTextureListener {
private static VideoPlayer player;
private MediaPlayer mediaPlayer;
private FrameLayout frameLayout;
private String playPath;// 播放路径
private Callback callback;// 回调
private Context context;
private ProgressBar progressBar;public interface Callback {
void onError(String msg);void onStarted(); void onComplete();
}
private VideoPlayer() {
}public static VideoPlayer getInstance() {
if (player == null) {
synchronized (VideoPlayer.class) {
if (player == null) {
player = new VideoPlayer();
}
}
}
return player;
}/**
-
播放视频
-
@param context
-
@param frameLayout
-
@param path
*/
public void play(Context context, FrameLayout frameLayout, String path) {
L.d(“VideoPlayer.play:” + path);
this.context = context.getApplicationContext();
this.playPath = path;
this.frameLayout = frameLayout;
if (frameLayout == null || path == null) return;
for (int i = 0; i < frameLayout.getChildCount(); i++) {
if (frameLayout.getChildAt(i) != null
&& (frameLayout.getChildAt(i) instanceof TextureView
|| frameLayout.getChildAt(i) instanceof ProgressBar)) {
frameLayout.removeView(frameLayout.getChildAt(i));
}
}
TextureView textureView = new TextureView(context);
textureView.setSurfaceTextureListener(this);
textureView.setLayoutParams(new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT
));this.progressBar = new ProgressBar(context);
FrameLayout.LayoutParams plp = new FrameLayout.LayoutParams(80, 80);
plp.gravity = Gravity.CENTER;
this.progressBar.setLayoutParams(plp);
frameLayout.addView(textureView);
frameLayout.addView(this.progressBar);
}
/**
- 停止播放
*/
public void stop() {
if (mediaPlayer != null) {
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
}
mediaPlayer.reset();
mediaPlayer.release();
mediaPlayer = null;
}
}
/**
- 是否循环播放-默认循环播放
- @param isLoop
*/
public void setLoop(boolean isLoop) {
if (mediaPlayer != null) {
mediaPlayer.setLooping(isLoop);
}
}
/**
- 设置回调
- @param callback
*/
public void setCallback(Callback callback) {
this.callback = callback;
}
/**
-
开始播放
-
@param surface
*/
private void startPlay(Surface surface) {
try {
if (mediaPlayer == null)
mediaPlayer = new MediaPlayer();mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mediaPlayer.setScreenOnWhilePlaying(true); if (playPath.startsWith("http:") || playPath.startsWith("https:")) { mediaPlayer.setDataSource(this.context, Uri.parse(playPath)); } else { mediaPlayer.setDataSource(playPath); } mediaPlayer.setSurface(surface); mediaPlayer.prepareAsync(); L.d("prepareAsync"); mediaPlayer.setOnPreparedListener(mp -> { this.progressBar.setVisibility(View.GONE); mp.start(); if (callback != null) { callback.onStarted(); } }); /** * Called to indicate an error. * * @param mp the MediaPlayer the error pertains to * @param what the type of error that has occurred: * <ul> * <li>{@link #MEDIA_ERROR_UNKNOWN 1} * <li>{@link #MEDIA_ERROR_SERVER_DIED 100} Media server died. In this case, the application must release the * * MediaPlayer object and instantiate a new one. * </ul> * @param extra an extra code, specific to the error. Typically * implementation dependent. * <ul> * <li>{@link #MEDIA_ERROR_IO} * <li>{@link #MEDIA_ERROR_MALFORMED} * <li>{@link #MEDIA_ERROR_UNSUPPORTED} * <li>{@link #MEDIA_ERROR_TIMED_OUT} * <li><code>MEDIA_ERROR_SYSTEM (-2147483648)</code> - low-level system error. * </ul> * @return True if the method handled the error, false if it didn't. * Returning false, or not having an OnErrorListener at all, will * cause the OnCompletionListener to be called. */ mediaPlayer.setOnErrorListener((mp, what, extra) -> { if (callback != null) { callback.onError("播放失败"); } L.e("mediaPlayer-err:what=" + what + "::extra=" + extra); return false; }); mediaPlayer.setOnInfoListener((mp, what, extra) -> { if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START && frameLayout != null) { frameLayout.setBackground(null); } return false; }); mediaPlayer.setOnCompletionListener(mp -> { if (callback != null) { callback.onComplete(); } }); mediaPlayer.setLooping(true);
} catch (IllegalStateException ie) {
ie.printStackTrace();
L.e(“mediaPlayer-IllegalStateException:” + ie.getMessage(), ie);
if (callback != null) {
callback.onError(“播放失败,播放器发生未知错误”);
}
} catch (IOException e) {
e.printStackTrace();
L.e(“mediaPlayer-IOException:” + e.getMessage(), e);
if (callback != null) {
callback.onError(“播放失败,播放器出错”);
}
}
}
@Override
public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {// 初始化完成
startPlay(new Surface(surface));
}@Override
public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {}
@Override
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
stop();
return false;
}@Override
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {}
} -