9.4 KiB
Raw Blame History

前言

默认存储数据路径C:\LiveDirectorSaved\Sequoia 操作方式:

  1. 4级中使用Ctrl + Shift + D勾选Sequoia编辑器后显示。
  2. Ctrl + 剪切轨道。

PS. 编辑器状态下可以打开Sequoia编辑器界面再进4级前Ctrl+Shift+D点击后就可以打开。

相关类

  • TSLiveDirector\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

播放逻辑

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()

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():

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()在创建newTaskCamTaskDataRPC之后最终会调用directorCamManager.RequestPGMTaskServerUnreliable()/RequestPVWTaskServer()进入导播系统循环。

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()

    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() 来发送新的任务。

    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. 至此就完成了数据部分的定义和代码。
  6. 录制逻辑需要首先创建对应的录制逻辑继承自ISequoiaTakeRecorder.

  7. 在SequoiaHelper.BuildTakeRecorders 中根据参数创建对应的recorder.