/*---------------------------------------------------------------- // Copyright (C) 2025 Beijing All rights reserved. // // Author: huachangmiao // Create Date: 2025/10/29 // Module Describe: Unity微信小程序Worker版本解码器 //----------------------------------------------------------------*/ // #define ENABLE_LOG //启用日志 using System; using System.Runtime.InteropServices; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using UnityEngine; using LeviathanVideo.Abstractions; //****************************************************************** // 微信小游戏Worker解码器 //****************************************************************** internal unsafe class LeviathanWorkerDecoder : ILeviathanDecoder { // 日志辅助方法 private static void Log(string message) { LeviathanLogConfig.Log("WorkerDecoder", message); } private static void LogWarning(string message) { LeviathanLogConfig.LogWarning("WorkerDecoder", message); } private static void LogError(string message) { LeviathanLogConfig.LogError("WorkerDecoder", message); } // 事件实现 public event Action OnFirstFrameDecoded; #if UNITY_WEBGL // JavaScript接口声明 - 对应jslib中的方法 [DllImport("__Internal")] private static extern void InitializeLeviathanWorkerSystem(); [DllImport("__Internal")] private static extern int CreateWorkerVideoInstance(); [DllImport("__Internal")] private static extern bool SetWorkerVideoData(int instanceId, byte[] data, int dataSize); [DllImport("__Internal")] private static extern bool PrepareWorkerVideo(int instanceId, bool fastDecode); [DllImport("__Internal")] private static extern bool PlayWorkerVideo(int instanceId); [DllImport("__Internal")] private static extern bool PauseWorkerVideo(int instanceId); [DllImport("__Internal")] private static extern bool ResumeWorkerVideo(int instanceId); [DllImport("__Internal")] private static extern bool StopWorkerVideo(int instanceId); [DllImport("__Internal")] private static extern bool UpdateWorkerVideo(int instanceId, float deltaTime); [DllImport("__Internal")] private static extern bool DestroyWorkerVideo(int instanceId); [DllImport("__Internal")] private static extern void SetWorkerVideoStatusCallback(int instanceId, System.Action callback); [DllImport("__Internal")] private static extern void SetWorkerVideoFirstFrameDisplayCallback(int instanceId, System.Action callback); // 改进的静态回调方法,支持实例ID参数 #if UNITY_WEBGL && !UNITY_EDITOR [AOT.MonoPInvokeCallback(typeof(System.Action))] #endif private static void StaticOnStatusChanged(int instanceId, int status) { // 直接通过instanceId查找对应的实例 if (_instances.ContainsKey(instanceId)) { var instance = _instances[instanceId]; if (instance != null && instance._isInitialized && !instance._isDisposing) { instance.HandleStatusChanged(status); } } else { LogWarning($"状态回调: 未找到实例ID {instanceId}"); } } #if UNITY_WEBGL && !UNITY_EDITOR [AOT.MonoPInvokeCallback(typeof(System.Action))] #endif private static void StaticOnFirstFrameDisplay(int instanceId) { // 直接通过instanceId查找对应的实例 if (_instances.ContainsKey(instanceId)) { var instance = _instances[instanceId]; if (instance != null && instance._isInitialized && !instance._isDisposing) { instance.HandleFirstFrameDisplay(); } } else { LogWarning($"首帧回调: 未找到实例ID {instanceId}"); } } [DllImport("__Internal")] private static extern int GetWorkerVideoFrameCount(int instanceId); [DllImport("__Internal")] private static extern long GetWorkerVideoDuration(int instanceId); [DllImport("__Internal")] private static extern int GetWorkerVideoWidth(int instanceId); [DllImport("__Internal")] private static extern int GetWorkerVideoHeight(int instanceId); [DllImport("__Internal")] private static extern int GetWorkerVideoYTextureWidth(int instanceId); [DllImport("__Internal")] private static extern int GetWorkerVideoYTextureHeight(int instanceId); [DllImport("__Internal")] private static extern int GetWorkerVideoUVTextureWidth(int instanceId); [DllImport("__Internal")] private static extern int GetWorkerVideoUVTextureHeight(int instanceId); [DllImport("__Internal")] private static extern void SetWorkerVideoTextureId(int instanceId, string type, int textureId); [DllImport("__Internal")] private static extern int GetWorkerVideoCurrentFrameIndex(int instanceId); [DllImport("__Internal")] private static extern bool SeekWorkerVideo(int instanceId, float timeMs); [DllImport("__Internal")] private static extern bool SeekWorkerVideoToFrame(int instanceId, int frameIndex); [DllImport("__Internal")] private static extern bool SetWorkerVideoPlaySpeed(int instanceId, float speed); [DllImport("__Internal")] private static extern bool SetWorkerVideoLooping(int instanceId, bool isLooping); [DllImport("__Internal")] private static extern bool SetWorkerVideoAlphaType(int instanceId, int alphaType); // 纹理上传相关接口 [DllImport("__Internal")] private static extern System.IntPtr GetWorkerVideoSharedYBuffer(int instanceId); [DllImport("__Internal")] private static extern System.IntPtr GetWorkerVideoSharedUBuffer(int instanceId); [DllImport("__Internal")] private static extern System.IntPtr GetWorkerVideoSharedVBuffer(int instanceId); [DllImport("__Internal")] private static extern int GetWorkerVideoSharedBufferSize(int instanceId, string type); [DllImport("__Internal")] private static extern bool NotifyWorkerVideoTextureReady(int instanceId); // 获取WASM内存指针接口(已废弃,保留用于向后兼容) [DllImport("__Internal")] private static extern System.IntPtr GetWorkerVideoWasmYBufferPtr(int instanceId); [DllImport("__Internal")] private static extern System.IntPtr GetWorkerVideoWasmUBufferPtr(int instanceId); [DllImport("__Internal")] private static extern System.IntPtr GetWorkerVideoWasmVBufferPtr(int instanceId); // 检查是否使用JS端GPU纹理上传(所有平台都使用) [DllImport("__Internal")] private static extern bool IsWorkerVideoUsingJSTextureUpload(int instanceId); #endif // 私有字段 private long _frameCount; private double _frameInterval; private string _codecName = "worker_decoder"; private long _duration; private long _decodedFrameIndex; private AVFrame* _videoFrame; private bool _isLooping = false; private bool _fastDecode = true; // 快速解码开关 #if UNITY_WEBGL // 销毁标志(防止销毁期间的回调继续执行) private bool _isDisposing = false; private int _instanceId = -1; private bool _isInitialized = false; private NativeArray _videoData; private bool _isWorkerSystemReady = false; // 实例管理 private static System.Collections.Generic.Dictionary _instances = new System.Collections.Generic.Dictionary(); // 帧数据缓存 private int _currentFrameWidth = 0; private int _currentFrameHeight = 0; private System.IntPtr _currentFrameData = System.IntPtr.Zero; // 内存共享缓冲区管理 private System.IntPtr _sharedYBuffer = System.IntPtr.Zero; private System.IntPtr _sharedUBuffer = System.IntPtr.Zero; private System.IntPtr _sharedVBuffer = System.IntPtr.Zero; private int _sharedYBufferSize = 0; private int _sharedUBufferSize = 0; private int _sharedVBufferSize = 0; private bool _sharedBuffersInitialized = false; private UnityEngine.Texture2D[] _textureReferences = null; // 帧上传追踪(避免重复上传同一帧) private long _lastUploadedFrameIndex = -1; // 是否使用JS端GPU纹理上传(所有平台都使用) private bool _useJSTextureUpload = false; // 视频状态枚举 private enum VideoStatus { Initialized = 0, Prepared = 1, Playing = 2, Paused = 3, Stopped = 4, Error = 5 } // 当前视频状态 private VideoStatus _currentStatus = VideoStatus.Initialized; // 静态初始化标志 private static bool _systemInitialized = false; #endif // 实现接口属性 public long FrameCount => _frameCount; public double FrameInterval => _frameInterval; public string CodecName => _codecName; public long Duration => _duration; public long DecodedFrameIndex => _decodedFrameIndex; public AVFrame* VideoFrame => _videoFrame; // 循环播放属性 public bool IsLooping { get => _isLooping; set { _isLooping = value; #if UNITY_WEBGL if (_instanceId >= 0 && _isWorkerSystemReady && _isInitialized) { SetWorkerVideoLooping(_instanceId, _isLooping); } else { Log($"循环播放设置延迟到初始化完成后: {_isLooping}"); } #endif } } public LeviathanWorkerDecoder() { // 分配AVFrame结构 _videoFrame = (AVFrame*)Marshal.AllocHGlobal(sizeof(AVFrame)); *_videoFrame = new AVFrame(); // 初始化Worker系统 InitializeWorkerSystem(); } private void InitializeWorkerSystem() { #if UNITY_WEBGL try { // 确保系统只初始化一次 if (!_systemInitialized) { Log("初始化Worker系统..."); InitializeLeviathanWorkerSystem(); #if ENABLE_LOG LeviathanLogConfig.EnableAll(); #endif _systemInitialized = true; _isWorkerSystemReady = true; Log("Worker系统初始化完成"); } else { _isWorkerSystemReady = true; Log("Worker系统已经初始化"); } } catch (Exception e) { LogError($"Worker系统初始化异常: {e.Message}"); _isWorkerSystemReady = false; } #endif } #if UNITY_WEBGL // 状态回调处理(实例方法) private void HandleStatusChanged(int status) { // 如果正在销毁,忽略所有状态变更 if (_isDisposing) { return; } _currentStatus = (VideoStatus)status; Log($"实例{_instanceId}状态变更: {_currentStatus}"); // 根据状态更新内部数据 switch (_currentStatus) { case VideoStatus.Prepared: // 视频准备完成,更新视频信息 UpdateVideoInfo(); break; case VideoStatus.Error: LogError($"实例{_instanceId}视频播放出错"); break; } } private void HandleFirstFrameDisplay() { // 如果正在销毁,忽略首帧回调 if (_isDisposing) { return; } Log($"实例{_instanceId}首帧显示"); // 触发第一帧回调 OnFirstFrameDecoded?.Invoke(); } #endif private void UpdateVideoInfo() { #if UNITY_WEBGL if (_instanceId >= 0) { _frameCount = GetWorkerVideoFrameCount(_instanceId); _duration = GetWorkerVideoDuration(_instanceId); _currentFrameWidth = GetWorkerVideoWidth(_instanceId); _currentFrameHeight = GetWorkerVideoHeight(_instanceId); // 获取纹理尺寸信息 int yTextureWidth = GetWorkerVideoYTextureWidth(_instanceId); int yTextureHeight = GetWorkerVideoYTextureHeight(_instanceId); int uvTextureWidth = GetWorkerVideoUVTextureWidth(_instanceId); int uvTextureHeight = GetWorkerVideoUVTextureHeight(_instanceId); // 更新AVFrame信息 if (_videoFrame != null) { _videoFrame->width = _currentFrameWidth; _videoFrame->height = _currentFrameHeight; // 设置linesize信息 - 这是关键修复 _videoFrame->linesize[0] = yTextureWidth; // Y平面的linesize _videoFrame->linesize[1] = uvTextureWidth; // U平面的linesize _videoFrame->linesize[2] = uvTextureWidth; // V平面的linesize Log($"更新视频信息: {_currentFrameWidth}x{_currentFrameHeight}, Y纹理: {yTextureWidth}x{yTextureHeight}, UV纹理: {uvTextureWidth}x{uvTextureHeight}"); } // 计算帧间隔 if (_frameCount > 0 && _duration > 0) { _frameInterval = (double)_duration / _frameCount / 1000000.0; // 转换为秒 } // 初始化共享缓冲区 InitializeSharedBuffers(); Log($"视频信息更新: {_currentFrameWidth}x{_currentFrameHeight}, 帧数: {_frameCount}, 时长: {_duration}"); } #endif } private void InitializeSharedBuffers() { #if UNITY_WEBGL if (_instanceId < 0 || _sharedBuffersInitialized) { return; } try { // 检查是否使用JS端GPU纹理上传 _useJSTextureUpload = IsWorkerVideoUsingJSTextureUpload(_instanceId); // 获取共享缓冲区指针和大小 _sharedYBuffer = GetWorkerVideoSharedYBuffer(_instanceId); _sharedUBuffer = GetWorkerVideoSharedUBuffer(_instanceId); _sharedVBuffer = GetWorkerVideoSharedVBuffer(_instanceId); _sharedYBufferSize = GetWorkerVideoSharedBufferSize(_instanceId, "Y"); _sharedUBufferSize = GetWorkerVideoSharedBufferSize(_instanceId, "U"); _sharedVBufferSize = GetWorkerVideoSharedBufferSize(_instanceId, "V"); if (_sharedYBuffer != System.IntPtr.Zero && _sharedUBuffer != System.IntPtr.Zero && _sharedVBuffer != System.IntPtr.Zero) { _sharedBuffersInitialized = true; Log($"共享缓冲区初始化成功: Y({_sharedYBufferSize}), U({_sharedUBufferSize}), V({_sharedVBufferSize})"); } else { if (_useJSTextureUpload) { Log($"使用JS端GPU纹理上传,跳过C#端纹理处理"); } else { LogWarning($"共享缓冲区不可用,使用传统纹理上传方式"); } _sharedBuffersInitialized = true; // 设置为true以启用纹理上传逻辑 } } catch (System.Exception e) { LogError($"初始化共享缓冲区异常: {e.Message}"); } #endif } public void SetTextureIds(UnityEngine.Texture2D[] textures) { #if UNITY_WEBGL if (_instanceId < 0 || textures == null || textures.Length < 3) { LogError("SetTextureIds: 参数无效"); return; } try { // 获取纹理的Native指针作为ID int yTextureId = (int)textures[0].GetNativeTexturePtr(); int uTextureId = (int)textures[1].GetNativeTexturePtr(); int vTextureId = (int)textures[2].GetNativeTexturePtr(); // 传递给JSLIB SetWorkerVideoTextureId(_instanceId, "Y", yTextureId); SetWorkerVideoTextureId(_instanceId, "U", uTextureId); SetWorkerVideoTextureId(_instanceId, "V", vTextureId); Log($"设置纹理ID成功: Y({yTextureId}), U({uTextureId}), V({vTextureId})"); // 如果共享缓冲区已初始化,开始纹理上传处理 if (_sharedBuffersInitialized) { StartTextureUploadProcessing(textures); } } catch (System.Exception e) { LogError($"SetTextureIds异常: {e.Message}"); } #endif } private void StartTextureUploadProcessing(UnityEngine.Texture2D[] textures) { #if UNITY_WEBGL if (!_sharedBuffersInitialized || textures == null || textures.Length < 3) { return; } // 存储纹理引用,在DecodeNextFrame中处理纹理上传 _textureReferences = textures; Log($"纹理上传处理已启动,纹理数量: {textures.Length}"); #endif } private unsafe void UploadTextureFromSharedBuffer(UnityEngine.Texture2D[] textures) { #if UNITY_WEBGL if (!_sharedBuffersInitialized || textures == null || textures.Length < 3) { return; } // 使用JS端GPU纹理上传,C#端不需要处理 if (_useJSTextureUpload) { return; } try { // 获取纹理尺寸 int yWidth = GetWorkerVideoYTextureWidth(_instanceId); int yHeight = GetWorkerVideoYTextureHeight(_instanceId); int uvWidth = GetWorkerVideoUVTextureWidth(_instanceId); int uvHeight = GetWorkerVideoUVTextureHeight(_instanceId); int ySize = yWidth * yHeight; int uvSize = uvWidth * uvHeight; // 检查是否有共享缓冲区(已废弃,保留用于向后兼容) if (_sharedYBuffer != System.IntPtr.Zero && _sharedUBuffer != System.IntPtr.Zero && _sharedVBuffer != System.IntPtr.Zero) { if (_sharedYBufferSize > 0) { NativeArray yData = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray( (void*)_sharedYBuffer, _sharedYBufferSize, Allocator.None); #if ENABLE_UNITY_COLLECTIONS_CHECKS NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref yData, AtomicSafetyHandle.Create()); #endif textures[0].SetPixelData(yData, 0); } if (_sharedUBufferSize > 0) { NativeArray uData = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray( (void*)_sharedUBuffer, _sharedUBufferSize, Allocator.None); #if ENABLE_UNITY_COLLECTIONS_CHECKS NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref uData, AtomicSafetyHandle.Create()); #endif textures[1].SetPixelData(uData, 0); } if (_sharedVBufferSize > 0) { NativeArray vData = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray( (void*)_sharedVBuffer, _sharedVBufferSize, Allocator.None); #if ENABLE_UNITY_COLLECTIONS_CHECKS NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref vData, AtomicSafetyHandle.Create()); #endif textures[2].SetPixelData(vData, 0); } // 注意:不在这里调用Apply,统一在LateUpdate的DisplayFrameApply中执行 // 这样可以让所有视频对象在同一时机上传GPU,减少CPU/GPU切换 } } catch (System.Exception e) { LogError($"纹理上传失败: {e.Message}"); } #endif } public int Init(NativeArray fileData, int beginFrameIndex = 1, bool syncDecodeFirstFrame = true, bool fastDecode = true) { #if UNITY_WEBGL _videoData = fileData; #endif _fastDecode = fastDecode; return InternalPlay(beginFrameIndex, syncDecodeFirstFrame); } public int InternalPlay(int beginFrameIndex = 1, bool syncDecodeFirstFrame = true) { #if UNITY_WEBGL try { // 检查Worker系统是否准备就绪 if (!_isWorkerSystemReady) { LogError("Worker系统未准备就绪,无法创建解码器"); return -1; } // 创建Worker视频实例 _instanceId = CreateWorkerVideoInstance(); if (_instanceId < 0) { LogError("创建Worker视频实例失败"); return -1; } // 注册实例 _instances[_instanceId] = this; // 先设置改进的静态回调(传递instanceId参数),确保在数据处理前回调已就绪 SetWorkerVideoStatusCallback(_instanceId, StaticOnStatusChanged); SetWorkerVideoFirstFrameDisplayCallback(_instanceId, StaticOnFirstFrameDisplay); Log($"实例{_instanceId}回调设置完成,开始设置视频数据"); // 设置视频数据 if (_videoData.IsCreated && _videoData.Length > 0) { // 将NativeArray转换为byte[] byte[] dataArray = _videoData.ToArray(); Log($"准备设置视频数据,大小: {dataArray.Length} bytes"); bool setDataResult = SetWorkerVideoData(_instanceId, dataArray, dataArray.Length); if (!setDataResult) { LogError("设置视频数据失败"); return -1; } Log($"视频数据设置成功"); } else { LogError("视频数据无效"); return -1; } // 准备视频 bool prepareResult = PrepareWorkerVideo(_instanceId, _fastDecode); if (!prepareResult) { LogError("准备视频失败"); return -1; } // 开始播放 bool playResult = PlayWorkerVideo(_instanceId); if (!playResult) { LogError("开始播放失败"); return -1; } // 设置循环播放状态(在播放开始后设置) if (_isLooping) { SetWorkerVideoLooping(_instanceId, _isLooping); } if (beginFrameIndex > 1) { SeekToFrame(beginFrameIndex); } _isInitialized = true; _lastUploadedFrameIndex = -1; // 重置帧上传追踪 Log($"Worker解码器初始化成功,ID: {_instanceId}, 数据大小: {_videoData.Length} bytes"); return 0; } catch (Exception e) { LogError($"Worker解码器初始化异常: {e.Message}"); return -1; } #else return 0; #endif } public int DecodeNextFrame() { #if UNITY_WEBGL // 如果正在销毁,立即返回 if (_isDisposing) { return -1; } if (!_isInitialized || _instanceId < 0 || !_isWorkerSystemReady) { return -1; } // 只有在Playing状态时才执行Update if (_currentStatus != VideoStatus.Playing) { // 非播放状态,返回0表示没有新帧但不是错误 return 0; } // Worker版本通过Update方法获取帧数据 float deltaTime = Time.deltaTime * 1000; // 转换为毫秒 bool updateResult = UpdateWorkerVideo(_instanceId, deltaTime); if (updateResult) { // 更新当前帧索引 _decodedFrameIndex = GetWorkerVideoCurrentFrameIndex(_instanceId); // 只有当帧索引发生变化时,才上传新的纹理数据(避免重复上传同一帧) if (_decodedFrameIndex != _lastUploadedFrameIndex && _textureReferences != null && _sharedBuffersInitialized) { try { UploadTextureFromSharedBuffer(_textureReferences); _lastUploadedFrameIndex = _decodedFrameIndex; // 注意:现在使用基于FPS的帧率控制,JS端会自动触发下一帧 // 不需要再调用NotifyWorkerVideoTextureReady } catch (System.Exception e) { LogError($"DecodeNextFrame纹理上传异常: {e.Message}"); } } return 0; } else { return -1; } #else return 0; #endif } public void InternalStop() { #if UNITY_WEBGL // 如果正在销毁,跳过Stop调用(Dispose会处理) if (_isDisposing) { Log($"实例{_instanceId}正在销毁,跳过InternalStop"); return; } if (_instanceId >= 0) { // 立即更新状态,防止DecodeNextFrame继续调用UpdateWorkerVideo _currentStatus = VideoStatus.Stopped; Log($"实例{_instanceId}状态立即设置为Stopped"); StopWorkerVideo(_instanceId); } _currentFrameData = System.IntPtr.Zero; _lastUploadedFrameIndex = -1; // 重置帧上传追踪 #endif } public int InternalRestart(NativeArray newData) { #if UNITY_WEBGL if (_instanceId >= 0 && _isInitialized) { // 先停止当前播放 StopWorkerVideo(_instanceId); // 清理旧的帧数据 _currentFrameData = System.IntPtr.Zero; _currentFrameWidth = 0; _currentFrameHeight = 0; _decodedFrameIndex = 0; _lastUploadedFrameIndex = -1; // 重置帧上传追踪 // 重置AVFrame信息 if (_videoFrame != null) { _videoFrame->width = 0; _videoFrame->height = 0; _videoFrame->linesize[0] = 0; _videoFrame->linesize[1] = 0; _videoFrame->linesize[2] = 0; _videoFrame->pts = 0; } _videoData = newData; Log($"Worker解码器重启: 清理旧帧数据,新数据大小: {newData.Length} bytes"); // 设置新的视频数据 byte[] dataArray = _videoData.ToArray(); bool setDataResult = SetWorkerVideoData(_instanceId, dataArray, dataArray.Length); if (!setDataResult) { LogError("重启时设置视频数据失败"); return -1; } // 重新准备和播放 bool prepareResult = PrepareWorkerVideo(_instanceId, _fastDecode); if (!prepareResult) { LogError("重启时准备视频失败"); return -1; } bool playResult = PlayWorkerVideo(_instanceId); if (!playResult) { LogError("重启时开始播放失败"); return -1; } return 0; } else { // 如果decoder未初始化,重新创建 Log($"Worker解码器重新创建: 数据大小: {newData.Length} bytes"); _videoData = newData; return InternalPlay(); } #else return 0; #endif } public void Seek(double ms) { #if UNITY_WEBGL if (_isInitialized && _instanceId >= 0 && _isWorkerSystemReady) { SeekWorkerVideo(_instanceId, (float)ms); _lastUploadedFrameIndex = -1; // Seek后重置帧上传追踪,确保下一帧会被上传 } #endif } public void SeekToFrame(long frame) { #if UNITY_WEBGL if (_isInitialized && _instanceId >= 0 && _isWorkerSystemReady) { SeekWorkerVideoToFrame(_instanceId, (int)frame); _lastUploadedFrameIndex = -1; // Seek后重置帧上传追踪,确保下一帧会被上传 } #endif } // 获取当前帧的数据指针 public System.IntPtr GetCurrentFrameDataPtr() { #if UNITY_WEBGL return _currentFrameData; #else return System.IntPtr.Zero; #endif } // 获取当前帧尺寸 public void GetCurrentFrameSize(out int width, out int height) { #if UNITY_WEBGL width = _currentFrameWidth; height = _currentFrameHeight; #else width = 0; height = 0; #endif } // 获取解码器状态 public int GetDecoderStatus() { #if UNITY_WEBGL if (_instanceId >= 0 && _isWorkerSystemReady) { // 根据当前状态返回对应的状态码 switch (_currentStatus) { case VideoStatus.Initialized: return 0; // 停止状态 case VideoStatus.Prepared: return 1; // 准备中 case VideoStatus.Playing: return 2; // 播放中 case VideoStatus.Paused: return 1; // 暂停状态 case VideoStatus.Stopped: return 0; // 停止状态 case VideoStatus.Error: return -1; // 错误状态 default: return -1; } } return -1; #else return 2; // 非WebGL平台默认返回已启动状态 #endif } // 等待解码器启动完成 public bool WaitForStartComplete(float timeoutSeconds = 5.0f) { #if UNITY_WEBGL if (_instanceId < 0 || !_isWorkerSystemReady) return false; float startTime = Time.time; while (Time.time - startTime < timeoutSeconds) { int status = GetDecoderStatus(); if (status == 2) // 已启动 { return true; } else if (status == -1) // 错误 { return false; } // 状态为0(停止)或1(启动中)时继续等待 } return false; // 超时 #else return true; // 非WebGL平台直接返回成功 #endif } public ref long GetDecodedFrameIndexRef() { return ref _decodedFrameIndex; } // 暂停/恢复播放的公共方法 public void Pause(bool pause) { #if UNITY_WEBGL if (_instanceId >= 0 && _isWorkerSystemReady) { if (pause) { // 立即更新状态为暂停 _currentStatus = VideoStatus.Paused; Log($"实例{_instanceId}状态立即设置为Paused"); PauseWorkerVideo(_instanceId); } else { // 恢复播放时,状态会通过Worker回调更新为Playing ResumeWorkerVideo(_instanceId); } } #endif } public void CopyFrameDataToTextures(UnityEngine.Texture2D[] textures) { // Worker解码器在UploadTextureFromSharedBuffer中已经调用了SetPixelData // 这里不需要额外操作 } public void ApplyTextures(UnityEngine.Texture2D[] textures) { #if UNITY_WEBGL // 使用JS端GPU纹理上传,C#端不需要调用Apply if (_useJSTextureUpload) { return; } // 统一在LateUpdate中执行Apply,避免多个视频对象频繁切换CPU/GPU if (textures != null && textures.Length >= 3) { if (_sharedYBufferSize > 0) textures[0].Apply(false, false); if (_sharedUBufferSize > 0) textures[1].Apply(false, false); if (_sharedVBufferSize > 0) textures[2].Apply(false, false); } #endif } public void Dispose() { #if UNITY_WEBGL // 立即设置销毁标志,防止任何回调或异步操作继续执行 _isDisposing = true; if (_instanceId >= 0) { // 立即更新状态,防止DecodeNextFrame继续调用UpdateWorkerVideo _currentStatus = VideoStatus.Stopped; Log($"实例{_instanceId}状态立即设置为Stopped (Dispose)"); // 从实例字典中移除,防止回调访问 _instances.Remove(_instanceId); // 销毁Worker视频实例(这会发送DESTROY消息到worker,清理WASM资源) DestroyWorkerVideo(_instanceId); _instanceId = -1; } _isInitialized = false; _isWorkerSystemReady = false; _currentFrameData = System.IntPtr.Zero; // 【修复内存泄漏】清理SharedBuffer指针引用 _sharedYBuffer = System.IntPtr.Zero; _sharedUBuffer = System.IntPtr.Zero; _sharedVBuffer = System.IntPtr.Zero; _sharedBuffersInitialized = false; // 【修复内存泄漏】清理纹理引用 _textureReferences = null; // 【修复内存泄漏】重置帧追踪 _lastUploadedFrameIndex = -1; // 清理视频数据 if (_videoData.IsCreated) { _videoData.Dispose(); } // 清理定时器 if (_statusCheckTimer != null) { _statusCheckTimer.Dispose(); _statusCheckTimer = null; } #endif if (_videoFrame != null) { Marshal.FreeHGlobal((System.IntPtr)_videoFrame); _videoFrame = null; } } // 状态检查相关 private System.Threading.Timer _statusCheckTimer; private void ___unuseFunction() { OnFirstFrameDecoded?.Invoke(); } }