219 lines
9.4 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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.

# 前言
默认存储数据路径C:\LiveDirectorSaved\Sequoia
操作方式:
1. 4级中使用`Ctrl + Shift + D`勾选Sequoia编辑器后显示。
2. Ctrl + 剪切轨道。
PS. 编辑器状态下可以打开Sequoia编辑器界面再进4级前Ctrl+Shift+D点击后就可以打开。
# 相关类
- TS`LiveDirector\Script\Sequoia`
- TsSequoiaManagerActor
- OnPlayButtonClicked()Sequoia播放函数。主要逻辑是打开Sequoia的序列化数据之后创建或取得播放器斌进行播放/停止。
- TsSequoiaData => USequoiaData => USequoiaObject
- TsSequoiaBinding => USequoiaBinding => UNameableSequoiaObject => USequoiaObject
- TsSequoiaTake => USequoiaTake => UNameableSequoiaObject => USequoiaObject
- *SequoiaDirectorCamTake*
- TsSequoiaTrack => USequoiaTrack => UNameableSequoiaObject => USequoiaObject
- CharacterLiveLinkAnimTrack
- SequoiaMotionTrack
- *SequoiaCamShotTrack*(SequoiaCamShotEvalTemplate)***相机Take的CamShot轨道。***
- *SequoiaCamTargetTrack*(SequoiaCamTargetEvalTemplate)***相机Take的Target轨道。***
- SequoiaAudioTrack
- TsSequoiaSection => USequoiaSection
- *SequoiaCamSection*(TS)
- *SequoiaCamTargetSection*(TS)
- TsSequoiaSectionWithFileRef
- CharacterLiveLinkAnimSection
- SequoiaMotionSection
- SequoiaAudioSection
- ISequoiaEvalTemplate
- *SequoiaCamShotEvalTemplate*
- *SequoiaCamTargetEvalTemplate*
- CharacterLiveLinkAnimEvalTemplate
- SequoiaMotionEvalTemplate
- SequoiaAudioEvalTemplate
- ICamShotEvalHandle
- *SingleCamShotEvalHandle*
- *DoubleCamShotEvalhandle*
- c++`LiveDirector\Source\Modules\Sequoia`
- SequoiaPlayer
- PlayInternal():播放逻辑,主要调用`SequoiaData->Evaluate();`
- USequoiaObject => UObject
# 播放逻辑
```c++
TsSequoiaManagerActor@OnPlayButtonClicked: start play : 大聲鑽石
[2024.11.26-04.21.03:648][613]Puerts: (0x00000BD7686682F0) SequoiaManager@ Composer: On start playing...
[2024.11.26-04.21.03:649][613]Puerts: (0x00000BD7686682F0) DirectorCamSequoiaHandle : Enter CamTarget Section: Idol.JiaRan
[2024.11.26-04.21.03:649][613]Puerts: (0x00000BD7686682F0) DirectorCamSequoiaHandle : play Cam Section: ZhuJiwei_Zheng16-24mm group:CC8F4D734664869EC8FE788E7550AC31 index:0 scrub:false
[2024.11.26-04.21.03:665][614]Puerts: (0x00000BD7686682F0) request PGM: WorkShop
```
1. Sequoia界面点击播放后调用TsSequoiaManagerActor::OnPlayButtonClicked()
2. SequoiaPlayer::PlayInternal(),设置时间范围后。
3. USequoiaData::Evaluate()。
1. 调用所有USequoiaBinding::Evaluate()。
1. 调用所有USequoiaTrack::Evaluate()。
2. 调用所有USequoiaTake::Evaluate()。
1. 调用所有USequoiaTrack::Evaluate()。
PS. 实际上Sequoia的镜头录制数据会创建SequoiaCamShotTrack、SequoiaCamTargetTrack轨道。
## USequoiaTrack::Evaluate()
```c++
void USequoiaTrack::Evaluate(TRange<FFrameTime> EvaluationRange, ESequoiaEvaluateType EvalType)
{
Super::Evaluate(EvaluationRange, EvalType);
TArray<FSequoiaEvalSection> EvalSections;
USequoiaUtil::GetEvalSections(Sections, EvaluationRange, EvalSections);//根据播放范围取得Section
OnEvaluate(EvalSections, EvaluationRange.GetLowerBoundValue(), EvaluationRange.GetUpperBoundValue(), EvalType);//调用蓝图类的BlueprintImplementableEvent事件。
}
```
在TsSequoiaTrack中Overrider了OnEvaluate():
```ts
OnEvaluate(EvalSections: $Ref<UE.TArray<UE.SequoiaEvalSection>>, EvalStartTime: UE.FrameTime, EvalEndTime: UE.FrameTime, EvalType: UE.ESequoiaEvaluateType) : void{
if(!this.CanEvaluate() || !EvalSections){
return
}
if(!this.evalTemplate){
this.evalTemplate = this.CreateTemplate()
if(!this.evalTemplate){
return
}
this.evalTemplate.InitTemplate(this)
}
let newEvalSections = new Array<TsSequoiaSection>()
let evalSectionsRef = $unref(EvalSections)
for(let index = 0; index < evalSectionsRef.Num(); index ++){
let sectionRef = evalSectionsRef.GetRef(index)
let tsSection = sectionRef.Section as TsSequoiaSection
if(!sectionRef || !tsSection){
continue
}
if(sectionRef.EvalMode == UE.ESequoiaEvaluateMode.EEM_Inside || sectionRef.EvalMode == UE.ESequoiaEvaluateMode.EEM_JumpIn){
newEvalSections.push(tsSection)
if(newEvalSections.length >= MAX_EVAL_COUNT){
break
}
}
}
let bTemplateSourceChanged = this.IsTemplateSourceChanged(newEvalSections)
if(bTemplateSourceChanged){
this.evalTemplate.SetTemplateSource(newEvalSections, EvalType)
}
this.evalTemplate.Evaluate(EvalStartTime, EvalEndTime, EvalType)
this.lastEvalType = EvalType
}
```
看得出主要是主要逻辑是:
1. 创建指定类型的evalTemplate之后调用`evalTemplate.InitTemplate()`。
2. 取得ESequoiaEvaluateMode为EEM_Inside与EEM_JumpIn的所有EvalSections。
3. 判断Template是否发生改变如果改变则调用`evalTemplate.SetTemplateSource()`。
4. 调用`evalTemplate::Evaluate()`。
## SequoiaCamSection =>
SequoiaCamSection => TsSequoiaSection。
- 数据Model类使用SequoiaCamSectionModel。
- SequoiaCamShotEvalTemplate
## ISequoiaEvalTemplateSequoiaCamShotEvalTemplate
- InitTemplate
- SetTemplateSource用来设置对应的*ICamShotEvalHandle*
- Evaluate
ISequoiaEvalTemplate => ICamShotEvalHandle.Eval()
在计算Section、以及FrameOffset参数之后调用***DirectorCamSequoiaHandle.PlayCamShotSection()***在创建newTask***CamTaskDataRPC***之后最终会调用directorCamManager.RequestPGMTaskServerUnreliable()/RequestPVWTaskServer()进入导播系统循环。
```ts
let newTask = new UE.CamTaskDataRPC()
newTask.WorkShopId = DirectorCamUtil.CopyGuid(workShopId)
newTask.CamGroupId = DirectorCamUtil.CopyGuid(camSection.camGroupId)
newTask.CamIndex = camSection.camIndex
newTask.StartFrame = camSection.GetStartFrameOffset().Value + frameOffset
newTask.bPreviewOneFrame = bPreviewOneFrame
```
## DirectorCamManager
1. this.RequestPVWTask(newPVWTaskData)
2. this.HandlePreStreamTaskDataMulticast(newPVWTaskData)
1. DirectorCamUtil.EnsureWorkShopReady()确保WorkShop有效并且已经初始化之后就调用对应函数。
1. DirectorEventSystem.Emit(this, DirectorEvent.OnPVWTaskRequested)
2. this.HandlePreStreamTaskByNetTag()
1. PVW => this.HandlePVWTask()
1. DirectorCamUtil.SubmitNewCommandIfDataNotChanged()
3.  this.RecordLastPVWTime()
### HandlePVWTask()
```ts
HandlePVWTask(): void {
if(!DirectorCamUtil.SubmitNewCommandIfDataNotChanged(this.preStreamTask, this.prestreamTaskData)){
if (this.preStreamTask) {
this.preStreamTask.Stop()
}
this.preStreamTask = DirectorCamUtil.CreateCamTask(this, this.prestreamTaskData, CamTaskType.FullStream, this.droneCamera,
this.PVWWindow, this.handHeldCamera)
if (this.preStreamTask) {
this.preStreamTask.Start()
if (this.PVWWindow) {
this.PVWWindow.SetViewBorderColor(0, new UE.LinearColor(0, 1, 0, 1))
}
console.log('PVW Task: ' + this.preStreamTask.workShop.BindPlacement.Title + " " + this.preStreamTask.groupName + " " +
this.preStreamTask.camName)
}
}
}
```
通过DirectorCamUtil.SubmitNewCommandIfDataNotChanged()判断CamTaskDataRPC是否相同如果Camera机位相同则直接提交。
如果不同,比如换机位了,就调用
`DirectorCamUtil.CreateCamTask() => this.preStreamTask.Start()`
来发送新的任务。
```ts
export function SubmitNewCommandIfDataNotChanged(task : DirectorCamTask, taskData : UE.CamTaskDataRPC):boolean{
let bSubmitSuccess = false
if(task && taskData){
let bWorkShopEqual = IsGuidEqual(task.workShopId, taskData.WorkShopId)
let bCamGroupEqual = IsGuidEqual(task.groupId, taskData.CamGroupId)
let bCamIndexEqual = task.GetMainCamIndex() == taskData.CamIndex
let bNotSpecialCam = taskData.CamIndex != HANDHELD_CAM_INDEX && taskData.CamIndex != DRONE_CAM_INDEX
if(bWorkShopEqual && bCamGroupEqual && bCamIndexEqual && bNotSpecialCam){
// submit new cmd
task.SubmitNewCommand(taskData)
bSubmitSuccess = true
}
}
return bSubmitSuccess
}
```
## 裁剪相关逻辑
DirectorCamSequoiaHandle.PlayCamShotSection()
# 其他
## 添加自定义轨道
往Sequoia添加一个自定义轨道可以按照以下大体步骤进行开发
1. 大部分的拓展逻辑都写在SequoiaCustomBuilderTool.ts
2. 在SequoiaCustomBuilderTools.ts 的BindingType,TrackType,SectionType中添加组定义类型.在关系Map(BindingToTrackMap)中添加从属关系
3. 在Sequoia代码文件夹下创建拓展文件夹创建对应的TsBinding,TsTrack,TsSection等对应的UObject以及Model类可以参考DirectorCam.
4. Model文件用于数据序列化和存储通常不要使用UE类型UObject文件是真正的逻辑类
5. 创建Binding和BindingModel类分别定义AssignModel和构造函数用来承接数据
1. 在SequoiaCustomBuildertool.CreateBindingModel 和 CreateEmptyBindingModelByBindingType中新增新类型的Model创建。
2. 在SequoiaCustomBuildertool.CreateBinding中添加新Binding类型的创建
3. Track,Take,Section也是类似于Binding的方式在CustomBuilderTool中添加创建代码。
4. 至此就完成了数据部分的定义和代码。
1. 录制逻辑需要首先创建对应的录制逻辑继承自ISequoiaTakeRecorder.
2. 在SequoiaHelper.BuildTakeRecorders 中根据参数创建对应的recorder.