Files
tysdk-intergration/sdk-intergration/LocalPackages/LeviathanVideo/Runtime/Platform/LeviathanWorkerDecoder.cs
2026-03-24 11:43:22 +08:00

1047 lines
34 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*----------------------------------------------------------------
// 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<int, int> callback);
[DllImport("__Internal")]
private static extern void SetWorkerVideoFirstFrameDisplayCallback(int instanceId, System.Action<int> callback);
// 改进的静态回调方法支持实例ID参数
#if UNITY_WEBGL && !UNITY_EDITOR
[AOT.MonoPInvokeCallback(typeof(System.Action<int, int>))]
#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<int>))]
#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<byte> _videoData;
private bool _isWorkerSystemReady = false;
// 实例管理
private static System.Collections.Generic.Dictionary<int, LeviathanWorkerDecoder> _instances =
new System.Collections.Generic.Dictionary<int, LeviathanWorkerDecoder>();
// 帧数据缓存
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<byte> yData = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<byte>(
(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<byte> uData = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<byte>(
(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<byte> vData = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<byte>(
(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<byte> 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<byte> 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(); }
}