1 构建SourceNode

本文详细介绍了音视频编解码过程中节点创建与初始化的具体步骤,包括根据文件格式选择合适的编解码器节点、构建节点信息及接口查询过程。

part 1:

在媒体文件格式被识别后就要根据文件格式创建节点,调用函数DoSetupSourceNode

PVPlayerEngine::DoSetupSourceNode(PVCommandId aCmdId, OsclAny* aCmdContext)

参数:

aCmdId:命令ID;

aCmdContext:

在实际的调用中,这两个参数分别是类PVPlayerEngineContext成员量

函数返回值:函数执行状态,成功与否;

本函数的作用就是根据源文件格式和输出格式,在注册的节点中查找相符合的,并创建源节点

1)调用函数PVPlayerNodeRegistry::QueryRegistry(PVMFFormatType& aInputType, PVMFFormatType& aOutputType, Oscl_Vector<PVUuid, OsclMemAllocator>& aUuids)

参数分别是源文件格式(输入格式),输出文件格式,保存UUID的vector量;根据输入、输出格式对,查找合适的Node并保存其UUID

2)调用函数PVPlayerNodeRegistry::CreateNode(PVUuid& aUuid)根据上面查到的UUID创建Node,函数返回指向类PVMFNodeInterface的指针;

3)将UUID和Node指针保存到结构体PVPlayerEngineUuidNodeMapping中,并存放到vector量iNodeUuids中;

4)构造类PVMFNodeSessionInfo,保存于Node有关的信息

5)调用函数PVMFNodeInterfaceImpl::Connect(const PVMFNodeSessionInfo &aSession),函数的参数就是上面构建的节点信息类,函数返回sessionID;此函数将aSession以及sessionID保存到一个指向类PVMFNodeSession指针变量中,并存储到类PVMFNodeInterface的成员vector量iSessions指向的内存中;

6)调用函数

PVMFCommandId PVMFNodeInterfaceImpl::QueryInterface(PVMFSessionId aSessionId,
        const PVUuid& aUuid,
        PVInterface*& aInterfacePtr,
        const OsclAny* aContext)

参数:

aSessionId:上面提到的ID;

aUuid:PVMF_DATA_SOURCE_INIT_INTERFACE_UUID;

aInterfacePtr:输出参数,目前为NULL;

aContext:指向类PVPlayerEngineContext,调用函数AllocateEngineContext构建,

AllocateEngineContext(NULL, iSourceNode, NULL, aCmdId, aCmdContext, PVP_CMD_SourceNodeQueryInitIF);

7)QueryInterface函数的构建一个cmd(PVMFNodeCommand),并将此命令添加到命令队列中进行处理;

调用函数cmd.PVMFNodeCommandBase::Construct(PVMFSessionId s, int32 aCmd, const PVUuid& aUuid,
                                                                              PVInterface*& aInterfacePtr,
                                                                                const OsclAny* aContext)

参数:

s:输入参数,同QueryInterface;

aCmd:输入参数,命令ID,PVMF_GENERIC_NODE_QUERYINTERFACE,指明此命令是查询接口;

aUuid、aInterfacePtr、aContext:输入参数,同QueryInterface;

此函数构建命令类型CMD,在构建时,

iParam1 = uuid.ALLOC_AND_CONSTRUCT(aUuid);
iParam2 = (OsclAny*) & aInterfacePtr;

Node命令类PVMFNodeCommand继承类PVMFNodeCommandBase;

顺便分析下函数AllocateEngineContext

PVPlayerEngine::AllocateEngineContext(PVPlayerEngineDatapath* aEngineDatapath, PVMFNodeInterface* aNode, PVPlayerDatapath* aDatapath, PVCommandId aCmdId, OsclAny* aCmdContext, int32 aCmdType)

参数:

aNode:Node接口指针;

aCmdId,aCmdContext:与函数DoSetupSourceNode参数相同;

aCmdType:命令类型;

函数返回指向类PVPlayerEngineContext的指针;

此函数功能就是构建了一个PVPlayerEngineContext,并将函数传递过来的参数保存,最后将此context加入到vector量iCurrentContextList中;

part 2:

在将命令加入到命令队列后,接下是处理,在其他的类中队命令的处理时调用类中成员函数Run(),但是在类PVMFNodeInterfaceImpl中却是调用其成员函数ProcessCommand()

1)首先会处理优先级较高的命令,如取消命令等;

2)再会处理正常普通命令;

3)在此进入函数DoQueryInterface(),是类PVMFNodeInterfaceImpl的成员函数,不过是虚函数,实际调用的是函数:

PVMFMP3FFParserNode::DoQueryInterface(),其中类PVMFMP3FFParserNode是MP3文件的解析Nodel类,类PVMFNodeInterfaceImpl是其众多父类中的一个,有如下继承关系:

22

4)函数返回,调用函数PVMFNodeInterfaceImpl::CommandComplete(iCurrentCommand, status, NULL, eventData),通知函数完成,

函数PVMFNodeInterfaceImpl::CommandComplete(PVMFNodeCommand& aCmd, PVMFStatus aStatus,
        PVInterface* aExtMsg, OsclAny* aEventData, PVUuid* aEventUUID, int32* aEventCode, int32 aEventDataLen)

参数:

aCmd:当前命令指针;aStatus:命令执行后的返回结果;

函数PVMFMP3FFParserNode::DoQueryInterface()

1)首先根据当前命令量调用函数PVMFNodeCommandBase::Parse(PVUuid*&aUuid, PVInterface**&aInterface),此函数仅是将前面保存的参数提取出来;

2)调用函数queryInterface(*uuid, *ptr)

PVMFMP3FFParserNode::queryInterface(const PVUuid& uuid, PVInterface*& iface)

参数:

uuid:输入参数,用于判断构造哪个接口指针;

iface:输出参数,指向构建的类;

此处UUID为PVMF_DATA_SOURCE_INIT_INTERFACE_UUID,构建指针指向类PVMFDataSourceInitializationExtensionInterface,但是在返回之前将其转化为PVInterface*类型,保存此指针在参数*ptr中;

3)函数返回;

PVMFCmdResp resp(aCmd.iId, aCmd.iContext, aStatus, extif, aEventData);

class DoubleLineEdge extends BaseEdge { protected getKeyPath(attributes) { // 获取源节点和目标节点 const { sourceNode, targetNode } = this; // 获取节点的位置坐标 const [x1, y1] = sourceNode.getPosition(); const [x2, y2] = targetNode.getPosition(); // 获取边数据中的线条数量和间距 const edgeData = this.context.graph.getEdgeData(this.id); const lineCount = edgeData.lineCount || 1; // 默认3条线 const spacing = edgeData.spacing || 10; // 默认间距3 // 计算线的方向向量 const dx = x2 - x1; const dy = y2 - y1; const length = Math.sqrt(dx * dx + dy * dy); // 计算垂直于线的单位向量 const unitPerpendicularX = dy / length; const unitPerpendicularY = -(dx / length); // 生成路径 const path = []; for (let i = 0; i < lineCount; i++) { // 计算当前线的偏移量(对称分布) const offset = (i - (lineCount - 1) / 2) * spacing; // 添加当前线的路径 path.push(['M', x1 + unitPerpendicularX * offset, y1 + unitPerpendicularY * offset]); path.push(['L', x2 + unitPerpendicularX * offset, y2 + unitPerpendicularY * offset]); } return path; } // 新增图片渲染方法 protected drawImageShape(attributes) { const { sourceNode, targetNode } = this; const [x1, y1] = sourceNode.getPosition(); const [x2, y2] = targetNode.getPosition(); // 计算中点位置(图片放置位置) const midX = (x1 + x2) / 2; const midY = (y1 + y2) / 2; // 从边数据获取图片配置(需在数据中定义) const edgeData = this.context.graph.getEdgeData(this.id); const imgCfg = edgeData.image || { url: 'default-icon.png', // 默认图片 width: 20, height: 20 }; // 创建/更新图片图形 this.upsertShape('image', { x: midX, y: midY, width: imgCfg.width, height: imgCfg.height, img: imgCfg.url, zIndex: 10 }); } } 将这个代码中 this.upsertShape('image', { x: midX, y: midY, width: imgCfg.width, height: imgCfg.height, img: imgCfg.url, zIndex: 10 });写个改成与 return path;类似
08-16
class DoubleLineEdge extends BaseEdge { protected getKeyPath(attributes) { // 获取源节点和目标节点 const { sourceNode, targetNode } = this; const [x1, y1] = sourceNode.getPosition(); const [x2, y2] = targetNode.getPosition(); // 获取边数据配置 const edgeData = this.context.graph.getEdgeData(this.id); const lineCount = edgeData.lineCount || 3; const spacing = edgeData.spacing || 8; // 计算方向向量 const dx = x2 - x1; const dy = y2 - y1; const length = Math.sqrt(dx * dx + dy * dy); const unitPerpendicularX = dy / length; const unitPerpendicularY = -dx / length; // 修复1:返回单一路径字符串(非对象数组) let path = ''; for (let i = 0; i < lineCount; i++) { const offset = (i - (lineCount - 1) / 2) * spacing; const startX = x1 + unitPerpendicularX * offset; const startY = y1 + unitPerpendicularY * offset; const endX = x2 + unitPerpendicularX * offset; const endY = y2 + unitPerpendicularY * offset; // 修复2:使用SVG路径字符串格式 if (i > 0) path += ' '; // 多条线用空格分隔 path += `M ${startX},${startY} L ${endX},${endY}`; } // 计算中点位置 const midX = (x1 + x2) / 2; const midY = (y1 + y2) / 2; // 获取图片配置 const imgCfg = edgeData.image || { url: 'default-icon.png', width: 20, height: 20 }; return path,{ x: midX - imgCfg.width / 2, y: midY - imgCfg.height / 2, width: imgCfg.width, height: imgCfg.height, img: imgCfg.url, zIndex: 10 }; } } 修改return path,{ x: midX - imgCfg.width / 2, y: midY - imgCfg.height / 2, width: imgCfg.width, height: imgCfg.height, img: imgCfg.url, zIndex: 10 }; }部分代码,使其可以在页面渲染
08-16
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值