// using System; using System; using System.Collections.Generic; using System.IO; // using System.IO; using FairyGUI; using FGUI.LG_Common; using FGUI.LG_live; using SGModule.Common.Extensions; using UnityEngine; using UnityEngine.Video; namespace RedHotRoast { public class LiveUI : BaseUI { private const int MaxVisibleCount = 6; // 一屏最多6个播放器 private const int PreloadCount = 2; // 上下各预加载一屏 private static readonly Dictionary dictionary_ = new(); private readonly Dictionary activeLoaders = new(); private readonly Dictionary _fileIsExist = new(); private LiveUICtrl ctrl; private float late_time = 0.1f; private List LiveConfig; private LiveModel model; private com_live ui; private GameObject videoParent; public LiveUI(LiveUICtrl ctrl) : base(ctrl) { uiName = UIConst.LiveUI; this.ctrl = ctrl; } protected override void SetUIInfo(UIInfo uiInfo) { uiInfo.packageName = "LG_live"; uiInfo.assetName = "com_live"; uiInfo.layerType = UILayerType.Popup; uiInfo.isNeedOpenAnim = false; uiInfo.isNeedCloseAnim = false; uiInfo.isNeedUIMask = true; } private void UpdateEvent() { late_time = 0.1f; } private void Refresh(object param = null) { var index = param.As(); // ui.list_.itemRenderer = RendererList; // Debug.Log($"LiveConfig.Count==2=== {LiveConfig.Count}"); // ui.list_.numItems = LiveConfig.Count; if (ui.list_.GetChildAt(index) is item_live item) { var livedata_ = PreDownloadManager.GetLiveDataByIndex(LiveConfig[index], index); item.state.selectedIndex = livedata_.progress >= 100 ? 0 : 1; item.img_mask.fillAmount = (float)(100 - livedata_.progress) / 100; BindPlayerToItem(item, index); if (LiveConfig[index].SubscribeUnlock == 1) { if (livedata_.progress < 100) item.vip.selectedIndex = 1; else item.vip.selectedIndex = 2; } else item.vip.selectedIndex = 0; } } // 初始化页面逻辑 private void InitView() { } #region 生命周期 protected override void OnInit() { videoParent = new GameObject("VideoPlayerParent"); VideoPlayerPool.Instance.Init(videoParent, MaxVisibleCount); } protected override void OnClose() { // 归还所有播放器 foreach (var kvp in dictionary_) VideoPlayerPool.Instance.ReturnPlayer(kvp.Value); dictionary_.Clear(); VideoPlayerPool.Instance.DisposeAll(); UnityEngine.Object.Destroy(videoParent); foreach (var t in loader_list) if (t != null && !t.isDisposed && t.texture != null) { t.texture.Dispose(); t.texture = null; } MemoryManager.CleanMemoryMonitor(); activeLoaders.Clear(); _fileIsExist.Clear(); GLoaderPool.Instance.DisposeAll(); } protected override void OnBind() { ui = baseUI as com_live; } private readonly List loader_list = new(); protected override void OnOpenBefore(object args) { LiveConfig = ConfigSystem.GetConfig(); ui.list_.itemRenderer = RendererList; Debug.Log($"LiveConfig.Count==1=== {LiveConfig.Count}"); ui.list_.numItems = LiveConfig.Count; ui.btn_close.SetClick(() => { CtrlCloseUI(); }); InitView(); // 滚动过程中实时刷新可见播放器 ui.list_.scrollPane.onScroll.Add(OnScrollUpdate); ui.list_.scrollPane.onScrollEnd.Add(OnScrollEnd); // 打开页面时初始化一次 OnScrollEnd(); } private void OnScrollUpdate() { if (LiveConfig == null || LiveConfig.Count == 0) return; var firstVisibleIndex = ui.list_.GetFirstChildInView(); var lastVisibleIndex = Mathf.Min(firstVisibleIndex + MaxVisibleCount - 1, ui.list_.numItems - 1); var startIndex = Mathf.Max(0, firstVisibleIndex - PreloadCount); var endIndex = Mathf.Min(ui.list_.numItems - 1, lastVisibleIndex + PreloadCount); // 回收超出范围 loader var keysToRemove = new List(); foreach (var kv in activeLoaders) { var idx = kv.Key; if (idx < startIndex || idx > endIndex) { GLoaderPool.Instance.ReturnLoader(kv.Value); var oldItem = ui.list_.GetChildAt(idx) as item_live; if (oldItem != null) { (oldItem.img_cover as com_loaderMask_circlle).loader = null; } keysToRemove.Add(idx); } } foreach (var k in keysToRemove) activeLoaders.Remove(k); // 分配 loader 并加载图片 for (var i = startIndex; i <= endIndex; i++) { if (activeLoaders.ContainsKey(i)) continue; var item = ui.list_.GetChildAt(i) as item_live; if (item == null) continue; if ((item.img_cover as com_loaderMask_circlle).loader != null && !(item.img_cover as com_loaderMask_circlle).loader.isDisposed) GLoaderPool.Instance.ReturnLoader((item.img_cover as com_loaderMask_circlle).loader); var loader = GLoaderPool.Instance.GetLoader(); (item.img_cover as com_loaderMask_circlle).loader = loader; item.img_cover.AddChild(loader); loader.SetSize(item.img_cover.width, item.img_cover.height); item.img_cover.visible = true; _fileIsExist.TryGetValue(i, out var value); if (!value) { var localPath = Path.Combine(TextureHelper.getResPath(), "LiveVideoCovers", LiveConfig[i].Name + "_cover" + ".jpg"); if (File.Exists(localPath)) { _fileIsExist[i] = true; Debug.Log($"[SetImgLoader] 本地存在,直接加载 {LiveConfig[i].Name + "_cover"}"); CrazyAsyKit.StartCoroutine(TextureHelper.LoadTexture(LiveConfig[i].Name + "_cover", loader, null, "LiveVideoCovers/")); } else { _fileIsExist[i] = false; CrazyAsyKit.StartCoroutine(LiveVideoManager.Instance.LoadCover(LiveConfig[i].Name + "_cover", tex => { if (tex != null) { if (loader != null) { loader.texture = new NTexture(tex); loader.visible = true; } } else { Debug.LogWarning("封面获取失败"); } })); } } else { CrazyAsyKit.StartCoroutine(TextureHelper.LoadTexture(LiveConfig[i].Name + "_cover", loader, null, "LiveVideoCovers/")); } activeLoaders[i] = loader; } } private HashSet lastVisibleItems = new(); private void OnScrollEnd() { var tasks = new List<(GLoader loader, string fileName, Action callback, string folder, string localFolder)>(); var firstVisibleIndex = ui.list_.GetFirstChildInView(); var lastVisibleIndex = Mathf.Min(firstVisibleIndex + MaxVisibleCount - 1, ui.list_.numItems - 1); var startIndex = Mathf.Max(0, firstVisibleIndex - PreloadCount); var endIndex = Mathf.Min(ui.list_.numItems - 1, lastVisibleIndex + PreloadCount); for (var i = startIndex; i <= endIndex; i++) { _fileIsExist.TryGetValue(i, out var value); if (value) continue; if (ui.list_.GetChildAt(i) is item_live item) { if ((item.img_cover as com_loaderMask_circlle).loader != null && !(item.img_cover as com_loaderMask_circlle).loader.isDisposed) GLoaderPool.Instance.ReturnLoader((item.img_cover as com_loaderMask_circlle).loader); var loader = GLoaderPool.Instance.GetLoader(); (item.img_cover as com_loaderMask_circlle).loader = loader; item.img_cover.AddChild(loader); loader.SetSize(item.img_cover.width, item.img_cover.height); var idx = i; tasks.Add((loader, LiveConfig[i].Name + "_cover", texture => { if (texture != null) _fileIsExist[idx] = true; }, "LiveAlbums/", FolderNames.VideoCoversName)); activeLoaders[i] = loader; } } TextureHelper.SetImgLoaders(tasks); Debug.Log($"[OnScrollEnd]==lastVisibleItems=== {lastVisibleItems.Count}"); VideoLoadScheduler.ClearAll(); // 1️⃣ 回收上一次的可见播放器 foreach (var oldItem in lastVisibleItems) if (dictionary_.TryGetValue(oldItem, out var player)) { player.Pause(); player.targetTexture?.Release(); VideoPlayerPool.Instance.ReturnPlayer(player); dictionary_.Remove(oldItem); oldItem.com_loader.visible = false; oldItem.img_cover.visible = true; } lastVisibleItems.Clear(); // 2️⃣ 获取当前可见项 var firstIndex = ui.list_.GetFirstChildInView(); var lastIndex = Mathf.Min(firstIndex + MaxVisibleCount, ui.list_.numItems); HashSet newVisibleItems = new(); for (var i = firstIndex; i < lastIndex; i++) { if (ui.list_.GetChildAt(i) is item_live item) { // 分配播放器 newVisibleItems.Add(item); if (!dictionary_.ContainsKey(item)) { var player = VideoPlayerPool.Instance.GetPlayer(); if (player != null) { dictionary_[item] = player; BindPlayerToItem(item, i); } } } } // 3️⃣ 保存为“上一次可见项” lastVisibleItems = newVisibleItems; } private void BindPlayerToItem(item_live item, int index) { var data = PreDownloadManager.GetLiveDataByIndex(LiveConfig[index], index); Debug.Log($"[绑定播放器 进度] progress==1=== {data.progress}"); if (data.progress < 100) return; var player = dictionary_[item]; var loader = item.com_loader.GetChild("loader") as GLoader; item.com_loader.visible = true; if (!loader_list.Contains(loader)) loader_list.Add(loader); TextureHelper.SetVideoLoader(player, loader, LiveConfig[index].Name, vp => { if (vp != null && !item.isDisposed) { if (data.progress >= 100) { item.img_cover.visible = false; GameDispatcher.Instance.Dispatch(GameMsg.liveVideoLoaded); } } }); } private void RendererList(int index, GObject obj) { var livedata_ = PreDownloadManager.GetLiveDataByIndex(LiveConfig[index], index); var item = (item_live)obj; // UI状态显示 item.state.selectedIndex = livedata_.progress < 100 ? 1 : 0; if (LiveConfig[index].SubscribeUnlock == 1) { if (livedata_.progress < 100) item.vip.selectedIndex = 1; else item.vip.selectedIndex = 2; } else item.vip.selectedIndex = 0; item.img_mask.fillAmount = (float)(100 - livedata_.progress) / 100; // 点击事件 item.SetClick(() => { if (GameHelper.GetLevel() < GameHelper.GetCommonModel().UnlockLive[1]) { if (DataMgr.IsUnlockLive.Value < 0 && !GameHelper.GetVipPrivilege(Subscription.UnlockLive.As())) UICtrlDispatcher.Instance.Dispatch(UICtrlMsg.SecretUnlockUI_Open, 2); else if (DataMgr.IsUnlockLive.Value == 0) UICtrlDispatcher.Instance.Dispatch(UICtrlMsg.UnlockTipsUI_Open, 2); else HandleDetailOpen(item, index); } else { HandleDetailOpen(item, index); } }); } private void HandleDetailOpen(item_live item, int index) { dictionary_.TryGetValue(item, out var player); // if (dictionary_.TryGetValue(item, out var player)) uiCtrlDispatcher.Dispatch(UICtrlMsg.LiveDetailUI_Open, (index, player)); } protected override void OnOpen(object args) { } protected override void OnHide() { } protected override void OnDisplay(object args) { } #endregion #region 消息 protected override void AddListener() { GameDispatcher.Instance.AddListener(GameMsg.LiveChange, Refresh); HallManager.Instance.UpdateEvent += UpdateEvent; } protected override void RemoveListener() { GameDispatcher.Instance.RemoveListener(GameMsg.LiveChange, Refresh); HallManager.Instance.UpdateEvent -= UpdateEvent; } #endregion } public class LiveData { public int AD_num; public int progress; public int Singleprogress; public long LiveADTime; } }