12 KiB
捕获参考
VideoDB 捕获会话的代码级详情。工作流程指南请参阅 capture.md。
WebSocket 事件
来自捕获会话和 AI 流水线的实时事件。无需 webhook 或轮询。
使用 scripts/ws_listener.py 连接并将事件转储到 ${VIDEODB_EVENTS_DIR:-$HOME/.local/state/videodb}/videodb_events.jsonl。
事件通道
| 通道 | 来源 | 内容 |
|---|---|---|
capture_session |
会话生命周期 | 状态变更 |
transcript |
start_transcript() |
语音转文字 |
visual_index / scene_index |
index_visuals() |
视觉分析 |
audio_index |
index_audio() |
音频分析 |
alert |
create_alert() |
警报通知 |
会话生命周期事件
| 事件 | 状态 | 关键数据 |
|---|---|---|
capture_session.created |
created |
— |
capture_session.starting |
starting |
— |
capture_session.active |
active |
rtstreams[] |
capture_session.stopping |
stopping |
— |
capture_session.stopped |
stopped |
— |
capture_session.exported |
exported |
exported_video_id, stream_url, player_url |
capture_session.failed |
failed |
error |
事件结构
转录事件:
{
"channel": "transcript",
"rtstream_id": "rts-xxx",
"rtstream_name": "mic:default",
"data": {
"text": "Let's schedule the meeting for Thursday",
"is_final": true,
"start": 1710000001234,
"end": 1710000002345
}
}
视觉索引事件:
{
"channel": "visual_index",
"rtstream_id": "rts-xxx",
"rtstream_name": "display:1",
"data": {
"text": "User is viewing a Slack conversation with 3 unread messages",
"start": 1710000012340,
"end": 1710000018900
}
}
音频索引事件:
{
"channel": "audio_index",
"rtstream_id": "rts-xxx",
"rtstream_name": "mic:default",
"data": {
"text": "Discussion about scheduling a team meeting",
"start": 1710000021500,
"end": 1710000029200
}
}
会话激活事件:
{
"event": "capture_session.active",
"capture_session_id": "cap-xxx",
"status": "active",
"data": {
"rtstreams": [
{ "rtstream_id": "rts-1", "name": "mic:default", "media_types": ["audio"] },
{ "rtstream_id": "rts-2", "name": "system_audio:default", "media_types": ["audio"] },
{ "rtstream_id": "rts-3", "name": "display:1", "media_types": ["video"] }
]
}
}
会话导出事件:
{
"event": "capture_session.exported",
"capture_session_id": "cap-xxx",
"status": "exported",
"data": {
"exported_video_id": "v_xyz789",
"stream_url": "https://stream.videodb.io/...",
"player_url": "https://console.videodb.io/player?url=..."
}
}
有关最新详情,请参阅 VideoDB 实时上下文文档。
事件持久化
使用 ws_listener.py 将所有 WebSocket 事件转储到 JSONL 文件以供后续分析。
启动监听器并获取 WebSocket ID
# Start with --clear to clear old events (recommended for new sessions)
python scripts/ws_listener.py --clear &
# Append to existing events (for reconnects)
python scripts/ws_listener.py &
或者指定自定义输出目录:
python scripts/ws_listener.py --clear /path/to/output &
# Or via environment variable:
VIDEODB_EVENTS_DIR=/path/to/output python scripts/ws_listener.py --clear &
脚本在第一行输出 WS_ID=<connection_id>,然后无限期监听。
获取 ws_id:
cat "${VIDEODB_EVENTS_DIR:-$HOME/.local/state/videodb}/videodb_ws_id"
停止监听器:
kill "$(cat "${VIDEODB_EVENTS_DIR:-$HOME/.local/state/videodb}/videodb_ws_pid")"
接受 ws_connection_id 的函数:
| 函数 | 用途 |
|---|---|
conn.create_capture_session() |
会话生命周期事件 |
| RTStream 方法 | 参见 rtstream-reference.md |
输出文件(位于输出目录中,默认为 ${XDG_STATE_HOME:-$HOME/.local/state}/videodb):
videodb_ws_id- WebSocket 连接 IDvideodb_events.jsonl- 所有事件videodb_ws_pid- 进程 ID,便于终止
特性:
--clear标志,用于在启动时清除事件文件(用于新会话)- 连接断开时,使用指数退避自动重连
- 在 SIGINT/SIGTERM 时优雅关闭
- 连接状态日志记录
JSONL 格式
每行是一个添加了时间戳的 JSON 对象:
{"ts": "2026-03-02T10:15:30.123Z", "unix_ts": 1772446530.123, "channel": "visual_index", "data": {"text": "..."}}
{"ts": "2026-03-02T10:15:31.456Z", "unix_ts": 1772446531.456, "event": "capture_session.active", "capture_session_id": "cap-xxx"}
读取事件
import json
import time
from pathlib import Path
events_path = Path.home() / ".local" / "state" / "videodb" / "videodb_events.jsonl"
transcripts = []
recent = []
visual = []
cutoff = time.time() - 600
with events_path.open(encoding="utf-8") as handle:
for line in handle:
event = json.loads(line)
if event.get("channel") == "transcript":
transcripts.append(event)
if event.get("unix_ts", 0) > cutoff:
recent.append(event)
if (
event.get("channel") == "visual_index"
and "code" in event.get("data", {}).get("text", "").lower()
):
visual.append(event)
WebSocket 连接
连接以接收来自转录和索引流水线的实时 AI 结果。
ws_wrapper = conn.connect_websocket()
ws = await ws_wrapper.connect()
ws_id = ws.connection_id
| 属性 / 方法 | 类型 | 描述 |
|---|---|---|
ws.connection_id |
str |
唯一连接 ID(传递给 AI 流水线方法) |
ws.receive() |
AsyncIterator[dict] |
异步迭代器,产生实时消息 |
CaptureSession
连接方法
| 方法 | 返回值 | 描述 |
|---|---|---|
conn.create_capture_session(end_user_id, collection_id, ws_connection_id, metadata) |
CaptureSession |
创建新的捕获会话 |
conn.get_capture_session(capture_session_id) |
CaptureSession |
检索现有的捕获会话 |
conn.generate_client_token() |
str |
生成客户端身份验证令牌 |
创建捕获会话
from pathlib import Path
ws_id = (Path.home() / ".local" / "state" / "videodb" / "videodb_ws_id").read_text().strip()
session = conn.create_capture_session(
end_user_id="user-123", # required
collection_id="default",
ws_connection_id=ws_id,
metadata={"app": "my-app"},
)
print(f"Session ID: {session.id}")
注意:
end_user_id是必需的,用于标识发起捕获的用户。用于测试或演示目的时,任何唯一的字符串标识符都有效(例如"demo-user"、"test-123")。
CaptureSession 属性
| 属性 | 类型 | 描述 |
|---|---|---|
session.id |
str |
唯一的捕获会话 ID |
CaptureSession 方法
| 方法 | 返回值 | 描述 |
|---|---|---|
session.get_rtstream(type) |
list[RTStream] |
按类型获取 RTStream:"mic"、"screen" 或 "system_audio" |
生成客户端令牌
token = conn.generate_client_token()
CaptureClient
客户端在用户机器上运行,处理权限、通道发现和流传输。
from videodb.capture import CaptureClient
client = CaptureClient(client_token=token)
CaptureClient 方法
| 方法 | 返回值 | 描述 |
|---|---|---|
await client.request_permission(type) |
None |
请求设备权限("microphone"、"screen_capture") |
await client.list_channels() |
Channels |
发现可用的音频/视频通道 |
await client.start_capture_session(capture_session_id, channels, primary_video_channel_id) |
None |
开始流式传输选定的通道 |
await client.stop_capture() |
None |
优雅地停止捕获会话 |
await client.shutdown() |
None |
清理客户端资源 |
请求权限
await client.request_permission("microphone")
await client.request_permission("screen_capture")
启动会话
selected_channels = [c for c in [mic, display, system_audio] if c]
await client.start_capture_session(
capture_session_id=session.id,
channels=selected_channels,
primary_video_channel_id=display.id if display else None,
)
停止会话
await client.stop_capture()
await client.shutdown()
通道
由 client.list_channels() 返回。按类型分组可用设备。
channels = await client.list_channels()
for ch in channels.all():
print(f" {ch.id} ({ch.type}): {ch.name}")
mic = channels.mics.default
display = channels.displays.default
system_audio = channels.system_audio.default
通道组
| 属性 | 类型 | 描述 |
|---|---|---|
channels.mics |
ChannelGroup |
可用的麦克风 |
channels.displays |
ChannelGroup |
可用的屏幕显示器 |
channels.system_audio |
ChannelGroup |
可用的系统音频源 |
ChannelGroup 方法与属性
| 成员 | 类型 | 描述 |
|---|---|---|
group.default |
Channel |
组中的默认通道(或 None) |
group.all() |
list[Channel] |
组中的所有通道 |
通道属性
| 属性 | 类型 | 描述 |
|---|---|---|
ch.id |
str |
唯一的通道 ID |
ch.type |
str |
通道类型("mic"、"display"、"system_audio") |
ch.name |
str |
人类可读的通道名称 |
ch.store |
bool |
是否持久化录制(设置为 True 以保存) |
没有 store = True,流会实时处理但不保存。
RTStream 和 AI 流水线
会话激活后,使用 session.get_rtstream() 检索 RTStream 对象。
关于 RTStream 方法(索引、转录、警报、批处理配置),请参阅 rtstream-reference.md。
会话生命周期
create_capture_session()
│
v
┌───────────────┐
│ created │
└───────┬───────┘
│ client.start_capture_session()
v
┌───────────────┐ WebSocket: capture_session.starting
│ starting │ ──> Capture channels connect
└───────┬───────┘
│
v
┌───────────────┐ WebSocket: capture_session.active
│ active │ ──> Start AI pipelines
└───────┬──────────────┐
│ │
│ v
│ ┌───────────────┐ WebSocket: capture_session.failed
│ │ failed │ ──> Inspect error payload and retry setup
│ └───────────────┘
│ unrecoverable capture error
│
│ client.stop_capture()
v
┌───────────────┐ WebSocket: capture_session.stopping
│ stopping │ ──> Finalize streams
└───────┬───────┘
│
v
┌───────────────┐ WebSocket: capture_session.stopped
│ stopped │ ──> All streams finalized
└───────┬───────┘
│ (if store=True)
v
┌───────────────┐ WebSocket: capture_session.exported
│ exported │ ──> Access video_id, stream_url, player_url
└───────────────┘