using System; using System.Collections; using System.Collections.Generic; using Kill.Core; using UnityEngine; using UnityEngine.UI; using UnityEngine.Video; namespace Kill.UI.Components { public class VideoCtrl : MonoBehaviour { public bool istest; public VideoPlayer videoPlayer; public bool needFullScreen = false; bool isFullScreen = false; /// /// 0:正常播放 1:全屏播放 /// public List renderTextures; public RawImage windowImage; public RawImage fullImage; /// 竖屏全屏UI(竖屏视频全屏时使用,在场景中配置) public RawImage portraitFullImage; /// 当前使用的全屏显示目标(根据视频宽高比选择) RawImage currentFullImage; public GameObject playButton; public GameObject pauseButton; public Text totalTimeText; public Text nowTime; public Slider slider; bool isplaying = false; bool ispause = true; Action playOver; string url; public GameObject loading; private void FixedUpdate() { if (isplaying) { OnVideoPlaying(); } } public void Init(string url, Action playOver) { if(loading) loading.SetActive(true); // 视频加载完成前隐藏窗口画面,避免显示上一个视频残留 if (windowImage != null) windowImage.gameObject.SetActive(false); // 停止播放状态与进度记录,避免加载期间用旧数据刷新进度条/时间 isplaying = false; lastFrame = 0; // 重置时间显示,避免加载期间显示上一个视频的旧时长 totalTime = 0; nowTime.text=SecondToHour(0); if (totalTimeText != null) totalTimeText.text=SecondToHour(0); if (slider != null) slider.value=0; this.playOver = playOver; this.url = url; SetVideo(url); if (playButton != null) { playButton.SetActive(false); } if (pauseButton != null) { pauseButton.SetActive(false); } ispause = true; } public void PlayVideo() { StartCoroutine(YieldPlay()); } public IEnumerator YieldPlay() { yield return new WaitForSeconds(0.1f); ispause = false; videoPlayer.Play(); if (playButton != null) { playButton.SetActive(false); } if (pauseButton != null) { pauseButton.SetActive(true); } isplaying = true; } public void PauseVideo() { ispause = true; videoPlayer.Pause(); isplaying = false; if (playButton != null) { playButton.SetActive(true); } if (pauseButton != null) { pauseButton.SetActive(false); } } public void RePlay() { videoPlayer.Stop(); videoPlayer.frame = 0; PlayVideo(); } public void SetVideo(string url) { videoPlayer.url = url; videoPlayer.prepareCompleted += VideoPrepared; videoPlayer.Prepare(); if (playButton != null) { playButton.SetActive(true); } } public void Stop() { videoPlayer.Stop(); videoPlayer.url = ""; isplaying = false; lastFrame = 0; nowTime.text=SecondToHour(0); // 停止时清空总时长与进度条,避免残留上一个视频的显示 totalTime = 0; if (totalTimeText != null) totalTimeText.text=SecondToHour(0); if (slider != null) slider.value=0; } int totalTime = 0; public void VideoPrepared(VideoPlayer player) { if (renderTextures != null && renderTextures.Count > 0) { foreach (var r in renderTextures) Destroy(r); renderTextures.Clear(); } renderTextures=new List(); // 窗口视图:完整显示视频并居中,保持比例,多余部分留黑(竖屏视频占满高度、两侧留黑) RenderTexture texture = new RenderTexture((int)player.width, (int)player.height, 1); renderTextures.Add(texture); player.targetTexture = texture; windowImage.texture = texture; // 视频加载完成,重新显示窗口画面 if (windowImage != null) windowImage.gameObject.SetActive(true); FitWindowAspect(windowImage, player.width / (float)Mathf.Max(1, (int)player.height)); if (needFullScreen) { // 根据视频宽高比选择全屏UI:竖屏视频用竖屏全屏UI,否则用默认全屏UI currentFullImage = (player.width < player.height && portraitFullImage != null) ? portraitFullImage : fullImage; // 全屏纹理按视频分辨率创建,保证宽高比正确、不变形 RenderTexture fullTexture = new RenderTexture((int)player.width, (int)player.height, 1); renderTextures.Add(fullTexture); currentFullImage.texture = fullTexture; // 显示区域铺满屏幕,保持视频比例,超出部分裁剪(cover模式:尽量铺满屏幕) float videoAspect = player.width / (float)Mathf.Max(1, (int)player.height); var fullRt = currentFullImage.rectTransform; var fullParent = fullRt.parent as RectTransform; currentFullImage.uvRect = new Rect(0, 0, 1, 1); if (fullParent != null && fullParent.rect.width > 0 && fullParent.rect.height > 0) { float parentW = fullParent.rect.width; float parentH = fullParent.rect.height; // 检测全屏UI是否旋转了90°(横屏视频旋转90°后铺满竖屏手机屏幕) float rotZ = Mathf.Abs(Mathf.DeltaAngle(fullRt.localEulerAngles.z, 0f)); bool rotated90 = rotZ >= 45f && rotZ <= 135f; if (rotated90) { // 旋转90°的UI:矩形宽高互换,旋转后视觉铺满屏幕 fullRt.anchorMin = new Vector2(0.5f, 0.5f); fullRt.anchorMax = new Vector2(0.5f, 0.5f); fullRt.pivot = new Vector2(0.5f, 0.5f); fullRt.sizeDelta = new Vector2(parentH, parentW); } else { fullRt.anchorMin = Vector2.zero; fullRt.anchorMax = Vector2.one; fullRt.pivot = new Vector2(0.5f, 0.5f); fullRt.sizeDelta = Vector2.zero; } // 按视频比例裁剪(cover):旋转后视频宽对应屏幕高,显示比例需取反 float frameAspect = rotated90 ? parentH / parentW : parentW / parentH; if (Mathf.Abs(frameAspect - videoAspect) >= 0.001f) { if (videoAspect > frameAspect) { // 视频相对更宽:裁剪左右 float uvW = frameAspect / videoAspect; currentFullImage.uvRect = new Rect((1 - uvW) / 2f, 0, uvW, 1); } else { // 视频相对更窄:裁剪上下 float uvH = videoAspect / frameAspect; currentFullImage.uvRect = new Rect(0, (1 - uvH) / 2f, 1, uvH); } } } } videoPlayer.frame = 0; if (slider != null) slider.value = 0; // 总时长计算:优先用 length(秒);切换视频时 frameCount 可能残留旧视频的帧数,作为兜底 float duration = (float)player.length; if (duration > 0) { totalTime = (int)(duration + 0.5f); } else if (player.frameCount > 0 && player.frameRate > 0) { totalTime = (int)(player.frameCount / player.frameRate + 0.5f); } else { totalTime = 0; } if (totalTimeText != null) totalTimeText.text = SecondToHour(totalTime); videoPlayer.prepareCompleted -= VideoPrepared; player.loopPointReached += VideoOver; if (playButton != null) playButton.SetActive(true); nowTime.text=SecondToHour(0); if(loading) loading.SetActive(false); } /// /// 窗口显示适配:在窗口内完整显示视频(contain模式)并居中,保持比例,多余部分留黑 /// 竖屏视频占满高度、两侧留黑;横屏视频占满宽度、上下留黑 /// 仅调整windowImage(铺满窗口的RawImage)自身的尺寸,不影响窗口UI位置 /// private void FitWindowAspect(RawImage image, float videoAspect) { if (image == null) return; var rt = image.rectTransform; var parent = rt.parent as RectTransform; float parentW = parent != null ? parent.rect.width : Screen.width; float parentH = parent != null ? parent.rect.height : Screen.height; float targetW, targetH; if (videoAspect >= parentW / parentH) { // 视频更宽:宽度铺满,高度按比例缩小,上下留黑 targetW = parentW; targetH = parentW / videoAspect; } else { // 视频更高:高度铺满,宽度按比例缩小,左右留黑 targetH = parentH; targetW = parentH * videoAspect; } rt.anchorMin = new Vector2(0.5f, 0.5f); rt.anchorMax = new Vector2(0.5f, 0.5f); rt.pivot = new Vector2(0.5f, 0.5f); rt.sizeDelta = new Vector2(targetW, targetH); rt.anchoredPosition = Vector2.zero; image.uvRect = new Rect(0, 0, 1, 1); } public void ShowAnimation(int dir) { Animation Anim = GetComponent(); if (Anim != null) { if (dir == -1) { Anim["视频出现"].time = Anim["视频出现"].clip.length; Anim["视频出现"].speed = -1; Anim.Play("视频出现"); } else { Anim["视频出现"].time = 0; Anim["视频出现"].speed = 1; Anim.Play("视频出现"); } } } long lastFrame = 0; /// /// 播放中事件(给进度条赋值) /// public void OnVideoPlaying() { if (slider == null) return; // 帧数据未就绪(重新播放/切换视频的瞬间 frameCount 可能为0),跳过本次刷新,避免除零产生 Infinity/NaN if (videoPlayer.frameCount <= 0) return; if (Mathf.Abs(videoPlayer.frame - lastFrame) < 1) return; slider.value = (videoPlayer.frame + 1) / (float)videoPlayer.frameCount; lastFrame = videoPlayer.frame; nowTime.text = SecondToHour((int)(totalTime * slider.value)); } /// /// 拖动进度条 /// public void PointDownUp() { //if (slider.value != lastValue) //{ // videoPlayer.frame = (long)(videoPlayer.frameCount * slider.value); //} if (slider.value >= 0.98f) { videoPlayer.Stop(); VideoOver(videoPlayer); return; } videoPlayer.frame = (long)(videoPlayer.frameCount * slider.value) - 1; nowTime.text = SecondToHour((int)(totalTime * slider.value)); if (!ispause) { videoPlayer.Play(); isplaying = true; } // TeacherServer.m_instance.SendVideoProcess((long)(videoPlayer.frameCount * slider.value) - 1); } public void PointDown() { videoPlayer.Pause(); isplaying = false; } public void Draging() { videoPlayer.frame = (long)(videoPlayer.frameCount * slider.value) - 1; nowTime.text = SecondToHour((int)(totalTime * slider.value)); } public void VideoOver(VideoPlayer vp) { vp.loopPointReached -= VideoOver; isplaying = false; if (playButton != null) { playButton.SetActive(true); } if (pauseButton != null) { pauseButton.SetActive(false); } // 全屏播放结束后自动退出全屏 if (isFullScreen) SetFullScreen(false); playOver?.Invoke(); // 播放结束不重新加载视频:停止并回到开头,重置UI,等待再次播放 videoPlayer.Stop(); videoPlayer.frame = 0; lastFrame = 0; nowTime.text = SecondToHour(0); if (slider != null) slider.value = 0; ispause = true; // 重新订阅播放结束事件,保证再次播放结束后仍能触发 vp.loopPointReached += VideoOver; } public void SetFullScreen(bool isFullScreen) { this.isFullScreen = isFullScreen; // 未设置过全屏目标时兜底用默认全屏UI if (currentFullImage == null) currentFullImage = fullImage; if (isFullScreen) { // 视频未加载完成时提示,避免全屏操作无效(此时 renderTextures 也未就绪) if (!videoPlayer.isPrepared || renderTextures == null || renderTextures.Count < 2) { ToastUI.Show("100332"); return; } #if UNITY_ANDROID AndroidStatusBar.statusBarState = AndroidStatusBar.States.Hidden; #endif videoPlayer.targetTexture = renderTextures[1]; currentFullImage.transform.parent.gameObject.SetActive(true); UIManager.Instance.AddBackAction(() => SetFullScreen(false)); if (ispause) PlayVideo(); } else { #if UNITY_ANDROID AndroidStatusBar.statusBarState = AndroidStatusBar.States.TranslucentOverContent; #endif videoPlayer.targetTexture = renderTextures[0]; currentFullImage.transform.parent.gameObject.SetActive(false); UIManager.Instance.RecoverBackAction(); } } /// /// 秒转换小时 /// /// /// public string SecondToHour(float time) { int hour = 0; int minute = 0; int second = 0; second = (int)time; if (second >= 60) { minute = second / 60; second = second % 60; } if (minute >= 60) { hour = minute / 60; minute = minute % 60; } string result = ""; if (hour > 0) { result += hour + ":"; } if (minute < 10) result += "0"; result += minute + ":"; if (second < 10) result += "0"; result += second.ToString(); return result; } } }