差别
这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
public:it:spice:codec-agent-trans [2022/02/28 10:39] – oakfire | public:it:spice:codec-agent-trans [2022/06/29 17:46] (当前版本) – [3.2 流程详解] oakfire | ||
---|---|---|---|
行 74: | 行 74: | ||
* 那么传输路径与其复用WebDAVChannel 不如直接创建新名称的 PortChannel | * 那么传输路径与其复用WebDAVChannel 不如直接创建新名称的 PortChannel | ||
+ | ===== 2 传输方案 ===== | ||
+ | |||
+ | ==== 2.1 方案一 ==== | ||
+ | 直接使用 PortChannel 协议进行视频重定向流传输路径 | ||
+ | |||
+ | * {{: | ||
+ | |||
+ | * 优点:只借用已有通道类型,不需要修改新增 SPICE 协议, guest与client之间的传输数据内容即可自行随意定义 | ||
+ | * 优点:不需要修改 spice-server 代码, | ||
+ | * 缺点:spice-gtk 客户端需要和 之前的 demo codec-agent 一样自行解码收到的视频数据,并根据坐标自行画在屏幕,并处理遮盖问题 | ||
+ | ==== 2.2 方案二 ==== | ||
+ | |||
+ | 直接把数据插入到主 DisplayChannel 的 视频流里传输 | ||
+ | * 优点:不需要动客户端代码 | ||
+ | * 缺点:需要修改 spice-server 代码,介入到主 DisplayChannel 对视频流的处理(涉及到播放区域与视频流编号), | ||
+ | * 缺点:相比方案一,遮盖计算从client 转移到了 server | ||
+ | * 缺点:如果不修改 server 与 client 之间的协议,只能传输符合 SPICE 协议格式的数据, 比如 视频流编码格式必须一致 | ||
+ | ==== 2.3 问题 ==== | ||
+ | |||
+ | * virtio 的传输速率是否足够快? 足够 | ||
+ | * virtio 是否足够稳定? | ||
+ | * portchannel 是否有足够稳定的用例? | ||
+ | ===== 3 PortChannel 详解 ===== | ||
+ | |||
+ | |||
+ | ==== 3.1 验证 ==== | ||
+ | |||
+ | 在试验时发现 spice-gtk 里的工具 spicy.c 已经有对特定名称为 org.spice.spicy 的 spiceport 的 传输测试代码,所以测试 portchannel 传输就比较简单了: | ||
+ | |||
+ | * guest Windows10虚拟机添加名为 org.spice.spicy 的 spiceport 通道设备:< | ||
+ | |||
+ | <channel type=" | ||
+ | <source channel=" | ||
+ | <target type=" | ||
+ | <alias name=" | ||
+ | <address type=" | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | * 用 VS 编译个测试串口程序, 编译为 TestPortChannel.exe, | ||
+ | |||
+ | #include < | ||
+ | #include < | ||
+ | //#define SPICE_PORT_NAME | ||
+ | #define SPICE_PORT_NAME_SPICY L" | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | HANDLE port_handle = :: | ||
+ | GENERIC_WRITE | GENERIC_READ, | ||
+ | 0, | ||
+ | NULL, | ||
+ | OPEN_EXISTING, | ||
+ | 0,// | ||
+ | NULL); | ||
+ | if (port_handle == INVALID_HANDLE_VALUE) { | ||
+ | std::cout << "open spice port failed! err:" << :: | ||
+ | }else { | ||
+ | std::cout << "open spice port successfully" | ||
+ | char write_buf[] = "hello troila!\n"; | ||
+ | const DWORD buf_size = sizeof(write_buf); | ||
+ | char read_buf[buf_size] = {0}; | ||
+ | DWORD size = 0; | ||
+ | DWORD totalsize = 0; | ||
+ | BOOL ret = :: | ||
+ | if (ret) { | ||
+ | std::cout << "write successfully" | ||
+ | } | ||
+ | else { | ||
+ | std::cout << "write failed! err:" << :: | ||
+ | goto end; | ||
+ | } | ||
+ | size = 0; | ||
+ | while (totalsize < buf_size) { | ||
+ | ret = :: | ||
+ | |||
+ | if (!ret) | ||
+ | { | ||
+ | std::cout << "write failed! err:" << :: | ||
+ | goto end; | ||
+ | } | ||
+ | |||
+ | totalsize += size; | ||
+ | std::cout << "read " << size << std::endl; | ||
+ | } | ||
+ | std::cout << "read content: " << read_buf << std::endl; | ||
+ | end: | ||
+ | ret = :: | ||
+ | std::cout << "close handle ret:" << ret << std::endl; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | * 在 client 端(Ubuntu 18.04) 安装 '' | ||
+ | * 在 guest 使用管理员权限执行 TestPortChannel.exe, 此时 spicy 日志显示对应 portchannel 打开并收到了 hello troila! | ||
+ | * 在 spicy 随意输入十几个字母,guest 端 TestPortChannel.exe 日志显示收到对应字母,并关闭串口< | ||
+ | |||
+ | open spice port successfully | ||
+ | write successfully | ||
+ | read 1 | ||
+ | ... | ||
+ | read content: hello guest!!! | ||
+ | close handle ret:1 | ||
+ | </ | ||
+ | |||
+ | * spicy 显示 对应 portchannel 已关闭< | ||
+ | |||
+ | ** Message: 10: | ||
+ | port 0x5561c6fcd270 org.spice.spicy: | ||
+ | hello troila! | ||
+ | port 0x5561c6fcd270 org.spice.spicy: | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== 3.2 流程详解 ==== | ||
+ | |||
+ | === 3.2.1 spice-gtk 对 port-channel 的处理流程 === | ||
+ | |||
+ | <uml> | ||
+ | @startuml spice-gtk-port-channel | ||
+ | |||
+ | skinparam sequenceMessageAlign center | ||
+ | skinparam shadowing false | ||
+ | |||
+ | header spice-gtk 中 port-channel 的处理流程 V0.1.0 by weiyongjiu | ||
+ | hide footbox | ||
+ | participant MainChannel | ||
+ | participant PortChannel | ||
+ | participant Spicy | ||
+ | ==port通道创建== | ||
+ | [-> MainChannel: | ||
+ | activate MainChannel | ||
+ | create PortChannel | ||
+ | MainChannel -> PortChannel: | ||
+ | deactivate MainChannel | ||
+ | |||
+ | PortChannel -> Spicy: signal " | ||
+ | activate PortChannel | ||
+ | activate Spicy | ||
+ | Spicy -> Spicy: channel_new() 进行初始化 | ||
+ | deactivate Spicy | ||
+ | deactivate PortChannel | ||
+ | |||
+ | ==数据传输== | ||
+ | |||
+ | [-> PortChannel: | ||
+ | activate PortChannel | ||
+ | PortChannel -> PortChannel: | ||
+ | PortChannel -> Spicy++: signal " | ||
+ | |||
+ | Spicy -> Spicy --: port_data() 输出数据到 stdin | ||
+ | deactivate PortChannel | ||
+ | |||
+ | @enduml | ||
+ | </ | ||
+ | |||
+ | PortChannel 对象构造过程: | ||
+ | * PortChannel 继承 SpiceChannel, | ||
+ | * spice_channel_constructed() | ||
+ | * spice_session_channel_new() | ||
+ | * g_signal_emit(session, | ||
+ | * SPICE_SESSION_CHANNEL_NEW " | ||
+ | * PortChannel 自身构造函数 spice_port_channel_class_init() | ||
+ | * channel_set_handlers() 设置以下消息对应的处理函数 | ||
+ | * SPICE_MSG_PORT_INIT | ||
+ | * SPICE_MSG_PORT_EVENT | ||
+ | * SPICE_MSG_SPICEVMC_DATA | ||
+ | * 消息对应的处理函数 port_handle_msg() | ||
+ | * g_coroutine_signal_emit() SPICE_PORT_DATA " | ||
+ | |||
+ | === 3.3 client 端 port-channel 的使用方式 === | ||
+ | |||
+ | * spice-gtk 实现了一个 SpicePortChannel, | ||
+ | * 增加对 glib 信号 '' | ||
+ | * 初始化时增加对该通道信号 '' | ||
+ | * 响应 '' | ||
+ | * 响应 '' | ||
+ | * 使用函数 '' |