video_recorder
摄像头录像保存为文件。
摄像头录像:把视频保存为 .mov 文件到 App Documents 目录。
边界:需要相机权限;统一 permission 的request("camera")暂不支持弹窗,在start()上用try/except VideoRecorderError处理失败。拍照见 camera / photos。录像只能放在用户操作回调里,不要在body()中调用。
#模块概览
| 项 | 说明 |
|---|---|
| 导入 | import video_recorder |
| 适合做什么 | 短视频采集、现场记录、后续 media_composer 剪辑 |
| 调用时机 | start() / stop() 放在按钮回调 |
| 推荐顺序 | start() → 轮询 status() → stop() 拿文件信息 |
| 有状态 | 支持 cancel() 丢弃半成品;不可重复 start() |
#快速开始
下面脚本开始录像、等待几秒后停止并打印文件信息:
已复制
import time
import video_recorder
try:
path = video_recorder.start()
print("录像中:", path)
time.sleep(3)
info = video_recorder.stop()
if info:
print("已保存:", info["path"])
print("时长:", info["duration"], "秒")
print("大小:", info["size"], "字节")
except video_recorder.VideoRecorderError as exc:
print("录像失败:", exc, f"code={exc.code}")
#AppUI 示例
开始/停止放在按钮回调;用 Timer 轮询录制时长。
已复制
import appui
import video_recorder
state = appui.State(
recording=False,
path="",
seconds=0.0,
status="点击开始录像",
)
def tick():
if not state.recording:
return
st = video_recorder.status()
state.seconds = float(st.get("duration", 0.0))
poll_timer = appui.Timer(interval=0.2, repeats=True, action=tick)
def toggle_recording():
if state.recording:
poll_timer.stop()
info = video_recorder.stop() or {}
state.batch_update(
recording=False,
path=info.get("path", ""),
status=f"已保存 · {info.get('duration', 0):.1f}s",
)
return
try:
path = video_recorder.start(camera="back")
state.batch_update(
recording=True,
path=path or "",
status="录像中…",
)
poll_timer.start()
except video_recorder.VideoRecorderError as exc:
state.status = f"无法开始: {exc}(请检查相机权限)"
def cancel_recording():
if not state.recording:
return
poll_timer.stop()
video_recorder.cancel()
state.batch_update(
recording=False,
path="",
seconds=0.0,
status="已取消并删除临时文件",
)
def body():
label = "停止录像" if state.recording else "开始录像"
return appui.NavigationStack(
appui.Form([
appui.Section("录像", [
appui.LabeledContent("状态", value=state.status),
appui.LabeledContent("时长", value=f"{state.seconds:.1f} 秒"),
appui.Text(state.path).font("caption").foreground_color("secondaryLabel"),
]),
appui.Section("操作", [
appui.Button(label, action=toggle_recording)
.button_style("bordered_prominent"),
appui.Button("取消录像", action=cancel_recording, role="destructive"),
], footer="真机测试最可靠;需相机权限。"),
]).navigation_title("录像")
)
appui.run(body, state=state)
#API 参考
#速查
| API | 作用 |
|---|---|
start(path, camera, quality) | 开始录像 → 文件路径 |
stop() | 停止 → {path, duration, size} |
cancel() | 停止并删除半成品 |
status() | {recording, duration, path} |
VideoRecorderError | 操作失败时抛出 |
#录像控制
start(path=None, camera="back", quality="high")
已复制
path = video_recorder.start()
path = video_recorder.start(camera="front")
| 参数 | 说明 |
|---|---|
path | 输出路径;省略时自动生成到 Documents |
camera | back(后置)/ front(前置) |
quality | 兼容参数,当前 Bridge 可能忽略 |
stop() — 返回 {path, duration, size};未录像时返回空字典。
cancel() — 丢弃半成品文件。
#状态
status() 返回:
| 字段 | 说明 |
|---|---|
recording | 是否正在录像 |
duration | 已录秒数 |
path | 当前输出路径 |
#异常
code | 含义 |
|---|---|
already_recording | 重复 start() |
unavailable | 相机不可用或无法添加输出 |
unknown_command | Bridge 命令错误 |
#常见错误
| 错误写法 | 后果 | 修正 |
|---|---|---|
在 body() 里 start() | 刷新时重复录像 | 放进按钮回调 |
if permission.request("camera"): | 判断错误且暂不支持 | 用 try/except 处理 start() |
放弃录像不 cancel() | 留下垃圾文件 | 调用 cancel() |
| 与 audio_recorder 同时录 | 硬件会话冲突 | 分开使用,先停一个 |
#相关文档
| 文档 | 用途 |
|---|---|
| camera | 拍照 |
| media_composer | 视频合并/导出 |
| avplayer | 播放录像文件 |
| permission | 相机权限状态查询 |
#预期效果
运行示例后,界面应出现文档描述的目标结果;若与预期不符,先看「失败路径」并按返回值或日志排查。