package com.qxcm.moduleutil.widget; import android.content.Context; import android.graphics.SurfaceTexture; import android.net.Uri; import android.opengl.GLES11Ext; import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.os.Handler; import android.os.Looper; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; import android.view.Surface; import android.view.View; import android.widget.FrameLayout; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.databinding.DataBindingUtil; import com.blankj.utilcode.util.PathUtils; import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.ui.PlayerView; import com.liulishuo.okdownload.DownloadTask; import com.liulishuo.okdownload.StatusUtil; import com.liulishuo.okdownload.core.cause.EndCause; import com.liulishuo.okdownload.core.cause.ResumeFailedCause; import com.liulishuo.okdownload.core.listener.DownloadListener1; import com.liulishuo.okdownload.core.listener.assist.Listener1Assist; import com.opensource.svgaplayer.SVGACallback; import com.opensource.svgaplayer.SVGADrawable; import com.opensource.svgaplayer.SVGADynamicEntity; import com.opensource.svgaplayer.SVGAImageView; import com.opensource.svgaplayer.SVGAParser; import com.opensource.svgaplayer.SVGAVideoEntity; import com.qxcm.moduleutil.R; import com.qxcm.moduleutil.databinding.RoomViewSvgaAnimBinding; import com.qxcm.moduleutil.utils.DownloadUtil; import com.qxcm.moduleutil.utils.Md5Utils; import com.qxcm.moduleutil.utils.SpUtil; import com.qxcm.moduleutil.utils.logger.Logger; import com.tencent.qgame.animplayer.AnimConfig; import com.tencent.qgame.animplayer.inter.IAnimListener; import com.tencent.qgame.animplayer.inter.IFetchResource; import java.io.File; import java.net.URL; import java.util.LinkedList; import java.util.Queue; public class AvatarFrameView extends FrameLayout implements IAnimListener { @Override public void onFailed(int i, @Nullable String s) { } @Override public boolean onVideoConfigReady(@NonNull AnimConfig animConfig) { return true; } @Override public void onVideoStart() { } @Override public void onVideoRender(int i, @Nullable AnimConfig animConfig) { } @Override public void onVideoComplete() { if (mType == 1) { mBinding.playView.startPlay(mFile); // 循环播放 } else { isPlaying = false; playNextFromQueue(); // 播放下一项 } } @Override public void onVideoDestroy() { } public enum RenderType {SVGA, MP4} private RenderType renderType; private ExoPlayer exoPlayer; private PlayerView playerView; private SVGAImageView svgaSurface; private GLSurfaceView glSurfaceView; private final Handler mainHandler = new Handler(Looper.getMainLooper()); private ChannelSplitRenderer1 renderer; private int mType;//1:循环播放 2:播放一次停止播放 private File mFile; private final Queue playQueue = new LinkedList<>(); private boolean isPlaying = false; private RoomViewSvgaAnimBinding mBinding; public AvatarFrameView(@NonNull Context context) { this(context, null); } public AvatarFrameView(@NonNull Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public AvatarFrameView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mBinding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.room_view_svga_anim, this, true); initViews(); } private void initViews() { // 初始化 ExoPlayer View playerView = new PlayerView(getContext()); playerView.setUseController(false); playerView.setVisibility(View.GONE); addView(playerView); // 初始化 SVGA View svgaSurface = new SVGAImageView(getContext()); svgaSurface.setVisibility(View.GONE); addView(svgaSurface); // // 初始化 GLSurfaceView // glSurfaceView = new GLSurfaceView(getContext()); // glSurfaceView.setEGLContextClientVersion(2); // renderer = new ChannelSplitRenderer1(); // glSurfaceView.setRenderer(renderer); // glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); // glSurfaceView.setVisibility(View.GONE); // 默认隐藏 // addView(glSurfaceView); // 初始化 ExoPlayer exoPlayer = new ExoPlayer.Builder(getContext()).build(); playerView.setPlayer(exoPlayer); mBinding.playView.setAnimListener(this); } private String getFileExtension(String url) { if (url == null || url.isEmpty()) return ""; int dotIndex = url.lastIndexOf("."); if (dotIndex > 0 && dotIndex < url.length() - 1) { return url.substring(dotIndex + 1).toLowerCase(); // 返回 "mp4", "svga" 等 } return ""; } private void playNextFromQueue() { PlayItem item = playQueue.poll(); if (item != null) { isPlaying = false; RenderType type = null; String ext = getFileExtension(item.url); if ("svga".equalsIgnoreCase(ext)) { type = RenderType.SVGA; } else if ("mp4".equalsIgnoreCase(ext)) { type = RenderType.MP4; } if (type == null) { isPlaying = false; playNextFromQueue(); // 跳过无效项 return; } clearPrevious(); renderType = type; mType = item.type; switch (type) { case SVGA: mBinding.playView.stopPlay(); mBinding.playView.setVisibility(View.GONE); loadSVGA(item.url); break; case MP4: mBinding.playView.setVisibility(View.VISIBLE); downloadAndPlayMp4(item.url); break; } } else { isPlaying = false; } } public void setSource(String url, int type2) { if (SpUtil.getOpenEffect()==1) { playQueue.offer(new PlayItem(url, type2)); if (!isPlaying) { playNextFromQueue(); } }else { playQueue.clear(); isPlaying = false; } // RenderType type = null; // if ("svga".equalsIgnoreCase(getFileExtension(url))){ // type = RenderType.SVGA; // }else if ("mp4".equalsIgnoreCase(getFileExtension(url))){ // type = RenderType.MP4; // } // // clearPrevious(); // renderType = type; // mType = type2; // switch (type) { // case SVGA: // mBinding.playView.stopPlay(); // mBinding.playView.setVisibility(View.GONE); // loadSVGA(url); // break; // case MP4: //// loadMP4(url); // mBinding.playView.setVisibility(View.VISIBLE); // downloadAndPlayMp4(url); // break; // } } private void downloadAndPlayMp4(String url) { String filePath = PathUtils.getInternalAppCachePath() + Md5Utils.getStringMD5(url) + ".mp4"; File file = new File(filePath); if (file.exists()) { playMp4(file); mFile = file; } else { DownloadTask task = new DownloadTask.Builder(url, PathUtils.getInternalAppCachePath() , Md5Utils.getStringMD5(url) + ".mp4") .setMinIntervalMillisCallbackProcess(100) .setPassIfAlreadyCompleted(false) .setAutoCallbackToUIThread(true) .build(); // if (StatusUtil.isCompleted(task)) { // playMp4(task.getFile()); // mFile = task.getFile(); // } else { task.enqueue(new DownloadListener1() { @Override public void taskStart(@NonNull DownloadTask task, @NonNull Listener1Assist.Listener1Model model) { Logger.e("taskStart", model); } @Override public void retry(@NonNull DownloadTask task, @NonNull ResumeFailedCause cause) { Logger.e("retry", cause); task.cancel(); // CrashReport.postCatchedException(new RuntimeException("下载文件重试:" + cause == null ? "" : cause.name())); } @Override public void connected(@NonNull DownloadTask task, int blockCount, long currentOffset, long totalLength) { Logger.e("connected", blockCount); } @Override public void progress(@NonNull DownloadTask task, long currentOffset, long totalLength) { Logger.e("progress", currentOffset); } @Override public void taskEnd(@NonNull DownloadTask task, @NonNull EndCause cause, @Nullable Exception realCause, @NonNull Listener1Assist.Listener1Model model) { Logger.e("taskEnd", model); playMp4(task.getFile()); mFile = task.getFile(); if (cause != null && cause != EndCause.COMPLETED) { // CrashReport.postCatchedException(new RuntimeException("下载任务结束:" + cause == null ? "" : cause.name() + "_realCause:" + realCause == null ? "" : realCause.getMessage())); } } }); // } } } private void playMp4(File file) { if (file != null) { mBinding.playView.startPlay(file); } else { // showAnim(); // playMp4(file); // CrashReport.postCatchedException(new RuntimeException("播放MP4失败:File is null")); } } private void loadSVGA(String url) { svgaSurface.setVisibility(View.VISIBLE); try { new SVGAParser(getContext()).parse(new URL(url), new SVGAParser.ParseCompletion() { @Override public void onComplete(SVGAVideoEntity videoItem) { SVGADrawable drawable = new SVGADrawable(videoItem, new SVGADynamicEntity()); svgaSurface.setImageDrawable(drawable); svgaSurface.setCallback(new SVGACallback() { @Override public void onStep(int i, double v) { } @Override public void onRepeat() { } @Override public void onPause() { } @Override public void onFinished() { isPlaying = false; playNextFromQueue(); } }); // svgaSurface.setCallback(new SVGAImageViewCallback() { // @Override // public void onAnimationFinished() { // isPlaying = false; // playNextFromQueue(); // } // }); svgaSurface.startAnimation(); } @Override public void onError() { isPlaying = false; playNextFromQueue(); } }); } catch (Exception e) { e.printStackTrace(); } } private void loadMP4(String url) { svgaSurface.setVisibility(View.GONE); playerView.setVisibility(View.GONE); glSurfaceView.setVisibility(View.VISIBLE); glSurfaceView.onResume(); glSurfaceView.requestRender(); // 使用 post 确保 GLSurfaceView 已完成 layout glSurfaceView.post(() -> { Log.d("@@@", "GLSurfaceView size after layout: " + glSurfaceView.getWidth() + "x" + glSurfaceView.getHeight()); glSurfaceView.onResume(); glSurfaceView.requestRender(); renderer.setOnSurfaceTextureReadyListener(surfaceTexture -> { mainHandler.post(() -> { Surface surface = new Surface(surfaceTexture); MediaItem mediaItem = MediaItem.fromUri(Uri.parse(url)); exoPlayer.setMediaItem(mediaItem); exoPlayer.setVideoSurface(surface); exoPlayer.prepare(); exoPlayer.play(); Log.d("@@@", "ExoPlayer state after play: " + exoPlayer.getPlaybackState()); }); }); }); } private void clearPrevious() { // if (exoPlayer != null) { // exoPlayer.stop(); // exoPlayer.clearVideoSurface(); // } if (svgaSurface.getDrawable() instanceof SVGADrawable) { ((SVGADrawable) svgaSurface.getDrawable()).stop(); } if (playerView != null) playerView.setVisibility(View.GONE); if (svgaSurface != null) svgaSurface.setVisibility(View.GONE); // if (glSurfaceView != null) glSurfaceView.setVisibility(View.GONE); // mBinding.playView.setVisibility(View.GONE); } public void release() { // if (exoPlayer != null) { // exoPlayer.release(); // } if (svgaSurface.getDrawable() instanceof SVGADrawable) { ((SVGADrawable) svgaSurface.getDrawable()).stop(); } if (glSurfaceView != null) { glSurfaceView.onPause(); // 必须调用生命周期方法 // glSurfaceView.requestRender(); } } public void clearQueue() { playQueue.clear(); isPlaying = false; } private static class PlayItem { String url; int type; PlayItem(String url, int type) { this.url = url; this.type = type; } } /** * 关闭特效 */ public void closeEffect() { //清空队列 playQueue.clear(); //关闭动画 // if (mBinding.image != null && isPlaying && mBinding.image.isAnimating()) { // mBinding.image.setAnimation(null); // mBinding.image.clearAnimation(); // mBinding.image.stopAnimation(); // } } }