---
title: 工作原理
description: 为什么 Pika 必须拆成 GNOME Shell 扩展和独立录制进程两半，两者如何通过 D-Bus 通信，门户采集与 GStreamer 编码管线是怎么搭起来的。
---

## 为什么是两个进程

Pika 由 GNOME Shell 扩展和一个独立的录制进程组成。这个划分不是可选的，
两个方向的约束各自成立：

**界面必须在 Shell 里。** Wayland 协议没有浮层和窗口定位能力 ——
官方 wayland-protocols 里没有 layer-shell 类协议，Mutter 也拒绝实现
`wlr-layer-shell`。实时选区遮罩、录制取景框、顶栏指示器，普通应用一个都画不出来。

**编码绝不能在 Shell 里。** GJS 是单线程的，和合成共用一个主循环 ——
在里面跑 H.264 整个桌面会掉帧。GNOME 自己也是这么分的
（`gnome-shell` + `org.gnome.Shell.Screencast`）。

结果是录制进程**没有任何窗口**，连设置和关于都在扩展的 `prefs.js` 里
（那是跑在独立进程的 GTK4，卡了也卡不到桌面）。

## 两半怎么说话

扩展调用录制进程的 D-Bus 接口 `io.github.hungtcs.Pika`；
录制进程需要选区时反过来调扩展的 `io.github.hungtcs.PikaShell`。

用方法调用而不是信号，是因为信号是广播、没有回执 —— 拿不到错误，
也没法触发按需启动。

录制进程通过 D-Bus 激活按需拉起，用户不需要手动启动任何东西。
激活文件里的 `SystemdService=` 是有讲究的：xdg-desktop-portal 靠 systemd
单元名反推应用 ID，而**门户的授权记忆是按应用 ID 存的**。少了这一行，
进程会落进调用方的 cgroup，门户认不出它，结果就是每次录制都重新弹授权框。

## 采集

采集走 `org.freedesktop.portal.ScreenCast`。门户返回的不是图像数据，
而是一个 **PipeWire node ID 加一个 fd**。

授权令牌持久化在 `$XDG_STATE_HOME/pika/screencast-restore-token`，
避免每次弹窗。用户随时可以撤销，所以每条采集路径都有重新申请的兜底。

**区域录制其实是「采整块屏 + 裁剪」** —— 门户不支持只采一块区域。

## 编码管线

```
pipewiresrc → queue → [videocrop] → vapostproc/videoconvert → 编码器 → h264parse
            → mp4mux → filesink
```

- 编码器按 `vah264lpenc` → `vah264enc` → `x264enc` → `openh264enc` 顺序探测，
  前两个是 VA 硬编。
- 有音频时并入 `audiomixer → audioconvert → audioresample → AAC → mux.audio_0`。
  即使只有一路来源也走 mixer —— 它会用静音填补空隙，麦克风掉线不会让时间轴缩短。
- 停止必须走 EOS 而不是直接关闭 —— `mp4mux` 要靠 EOS 才会写完索引。
  分片模式是保底：被外部掐断时文件仍然可播。

## 配置怎么共享

`io.github.hungtcs.Pika.gschema.xml` 是唯一事实来源，构建时分别拷进扩展目录和
应用自己的 schema 目录。

必须拷两份，是因为扩展跑在宿主 Shell 里、应用（将来）跑在沙箱里，
两边看不见对方的文件系统。schema 只是元数据，真正的数据在 dconf 里 —— 那是通的。
所以两份的 `id` 和 `path` 逐字一致。
