CRIWARE Unity Plugin Manual  Last Updated: 2024-07-12
使用Player类进行视频播放
CriManaMovieMaterial 和 CriManaMovieController 等组件实现了与 Unity 应用程序生命周期相匹配的CRI Sofdec视频播放处理。
通过使用这些,您可以轻松播放视频。

还可以使用Player(CriWare.CriMana.Player) 类独立实现类似的实现,而不使用插件提供的组件。
根据需要使用它,例如根据应用程序规范详细控制屏幕更新的时间时。

本章介绍使用Player类进行视频播放处理。

视频播放处理的实现

处理整体图

使用Player类的视频播放处理是通过以下流程实现的。
  • 生成Player
  • 设置要播放的文件/数据
  • 开始玩
  • 定期更新Player状态和帧数
  • 停止播放并销毁Player

生成Player

从脚本生成 CriWare.CriMana.Player
确保在管理播放的类中维护一个实例。

CriMana.Player player;
void SetupPlayer()
{
player = new CriMana.Player();
}

设置Player要播放的文件/数据

设置Player要播放的文件或内存中的数据。
在内存中设置数据时,显式固定内存以避免由于垃圾收集而导致内存移动。
// 指定文件路径时
void SetFilePath()
{
player.SetFile(null, System.Path.Combine(Application.temporaryCachePath, "FileName.usm"));
}

System.Runtime.InteropServices.GCHandle gcHandle;
// 当指定内存中的数据时
void SetData()
{
gcHandle = System.Runtime.InteropServices.GCHandle.Alloc(buffer, System.Runtime.InteropServices.GCHandle.Pinned);
player.SetData(gcHandle.AddrOfPinnedObject(), buffer.LongLength);
}

命令Player进行游戏

指示Player准备播放,并在准备好后开始播放。
使用CRI Sofdec播放视频时,从播放开始命令(Start 函数)到实际绘制之间存在时间滞后。
因此,我们建议您调用播放准备命令(PrepareForRendering函数),其中包括绘图准备,然后调用Start函数并同时启用绘图。

IEnumerator StartPlaybackVideo()
{
// 准备播放
player.PrepareForRendering();
var status = player.status;
// 等待准备完毕
while (status != CriWare.CriMana.Player.Status.ReadyForRendering)
{
if (status == CriWare.CriMana.Player.Status.Error)
{
player.Stop();
yield break;
}
yield return null;
// 更新状态
UpdatePlayer();
status = player.status;
}
// 开始播放并显示
player.Start();
SetMaterialToTargetObject();
}

更新Player状态和框架

Player类执行以下有关视频播放的处理。
1.Player状态更新 2.纹理绘制更新
  1. 将图画反映在材质上
相应地调用更新函数,如下所示。
更新函数必须在所有状态下定期调用,例如在 MonoBehaviour.Update 中:“准备播放”、“播放中”和“处理播放完成”。

Material material;
void UpdatePlayer()
{
// 1. 更新Player状态
player.Update();
// 2.更新纹理绘制
player.OnWillRenderObject(null);
// 3. 将绘图反映到材质上
isMaterialUpdated = player.UpdateMaterial(material);
}

CriWare.CriMana.Player.UpdateMaterial 函数的返回值指示是否已针对作为参数传递的材质反映了适当的设置和纹理。
如果这个返回值为true,那么使用Material进行绘制是没有问题的。
如果为假,则有可能绘制错误,请注意不要显示。

UnityEngine.UI.Graphic target;
Material originalMaterial;
void SetMaterialToTargetObject()
{
// 当 UpdateMaterial 处于成功状态时应该调用
UnityEngine.Debug.Assert(isMaterialUpdated);
if (originalMaterial == null)
{
// 记住原始材质
originalMaterial = target.material;
}
target.material = material;
}

停止播放并销毁Player

通过调用 CriWare.CriMana.Player.Stop 函数停止播放视频。
播放开始和停止之间存在时间差,因此请仔细检查状态。
另外,由于以Stop函数启动的停止进程也会破坏与绘制相关的资源,因此Material的状态从被调用的那一刻起就变得不确定。
在调用 Stop 函数的同时禁用使用材质的显示。

停止的Player可以重复使用,但是当您不再使用它时,必须显式调用 CriWare.CriMana.Player.Dispose 函数将其丢弃。

void StopAndDisposePlayer()
{
// 与停止命令同时禁用材质的显示
player.Stop();
ResetMaterialOfTargetObject()
var status = player.status;
// 等待停止完成
while (status != CriWare.CriMana.Player.Status.Stop)
{
yield return null;
// 更新状态
UpdatePlayer();
status = player.status;
}
// プレーヤーを破棄する
player.Dispose();
player = null;
if (gcHandle.IsAllocated())
{
gcHandle.Free();
}
}
void ResetMaterialOfTargetObject()
{
if (originalMaterial != null)
{
// 设置原始材质
target.material = originalMaterial;
originalMaterial = null;
}
}

响应应用程序状态变化

Unity 应用程序根据用户操作和系统行为更改状态。
为了稳定地播放视频,需要对这种状态变化做出适当的响应。

对姿势的反应

如果您的应用程序在视频播放时暂停,您也应该暂停视频播放。
如果没有正确暂停, CRI Sofdec库引用的时间将会提前,从而导致应用程序取消暂停后视频加速等问题。

由于某些平台在暂停和恢复时需要特殊处理,因此Player类提供了专用的暂停函数 ( CriWare.CriMana.Player.PauseOnApplicationPause )。
让我们在 MonoBehaviour.OnApplicationPause 函数中调用它。

void OnApplicationPause(bool flag)
{
player.PauseOnApplicationPause(flag);
}

高级用法

即使播放停止我也想继续绘图

前面提到,当调用CriWare.CriMana.Player.Stop函数时,Material的绘制内容就变成了未定义。
但是,如下所示,在某些情况下,即使视频已停止播放,您也希望继续显示材质。
  • 通过搜索移动播放位置然后重新播放
  • 切换到另一个视频并再次播放
在这种情况下,请停止使用 CriWare.CriMana.Player.StopForSeek 函数。
材质保持可见(CriWare.CriMana.Player.UpdateMaterial 返回 true)并且可以开始进一步的播放处理。

请注意,调用 StopForSeek 后播放的文件必须与停止前的文件具有相同的编解码器和分辨率。
如果不同,则需要重新生成内部资源,这可能会导致绘制不正确。

我想对帧更新时间进行详细控制

播放Sofdec视频时,显示的帧会与系统时间或视频文件中包含的音频的播放时间同步更新。
根据游戏过程中的表现,有时您需要在特定的时间更新帧,如下所示。
  • 我想根据视频文件外的音频播放时间提前播放。
  • 为了与其他性能同步,我想在每次应用程序帧更新时一次播放一帧。 。 在这种情况下,设置Player的主定时器类型,然后调用相应的状态更新函数。

    请注意,如果更改默认的主计时器类型,请确保视频文件不包含音频。
    视频帧和音频进程可能会不同步,这可能会妨碍正常播放。

    按用户时间更新

如果您想与唯一的定时器(例如另一个模块的播放时间)同步播放视频,请根据用户时间更新状态。
调用 CriWare.CriMana.Player.UpdateWithUserTime 函数而不是常规 Update 函数。

// 开始播放之前:设置使用用户时间
player.SetMasterTimerType(CriWare.CriMana.Player.TimerType.User);

// 记录播放开始时间
float startTime;
void UpdatePlayer()
{
// 更新时:通过指定用户时间更新
var playbackTime = Time.time - startTime;
var playbackTimeUlong = (ulong)(playbackTime * 1000.0f);
player.UpdateWithUserTime(playbackTimeUlong, 1000ul);
player.OnWillRenderObject(null);
isMaterialUpdated = player.UpdateMaterial(material);
}

使用手动计时器更新

如果您希望每次更新时都以固定宽度继续播放(例如每个应用程序帧一帧),请使用手动计时器进行更新。
调用 CriWare.CriMana.Player.UpdateWithManualTimeAdvanced 函数而不是常规 Update 函数。
每次调用 UpdateWithManualTimeAdvanced 函数都会提前时间,因此请注意仅在播放开始后的适当时间调用更新函数。

// 开始播放之前:设置为使用手动定时器
player.SetMasterTimerType(CriWare.CriMana.Player.TimerType.Manual);
// 开始播放前:设置手动定时器的时间更新单位
var fps = (ulong)UnityEngine.Application.targetFrameRate;
player.SetManualTimerUnit(1ul, fps);

void UpdatePlayer()
{
// 更新时:一边推进手动定时器一边更新
player.UpdateWithManualTimeAdvanced();
player.OnWillRenderObject(null);
isMaterialUpdated = player.UpdateMaterial(material);
}

   THIS SERVICE MAY CONTAIN TRANSLATIONS POWERED BY GOOGLE. GOOGLE DISCLAIMS ALL WARRANTIES RELATED TO THE TRANSLATIONS, EXPRESS OR IMPLIED, INCLUDING ANY WARRANTIES OF ACCURACY, RELIABILITY, AND ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.