开发者文档

Python API 与 IPC

用脚本或流水线应用程序操控 Bernard Review。包括协议、Python 客户端、按系列划分的所有命令、事件以及完整示例。

Bernard Review 可以由 Python、Maya、Nuke、Houdini、生产 tracker,或任何能打开 TCP 连接的语言来操控。艺术家手动完成的每一件事都有对应的命令:打开片段、播放、对比、调色、导出、开始会话、发布备注。本页足以让您实现播放器的自动化;逐字段的完整参考是 API 参考(英文;也可通过 Help > API Documentation 打开)。

协议

  • 地址:127.0.0.1,默认端口 5556。使用 --ipc-port N 启动的播放器监听 N:这样同一台机器上就可以运行两个播放器。
  • 格式:JSON Lines。每个请求是单独一行的一个 JSON 对象,以 \n 结尾,带有 "cmd" 字段:{"cmd": "seek", "frame": 24}。
  • 每个请求对应一个应答,按发送顺序返回,通常在几毫秒之内:{"status": "ok", "cmd": "seek", ...} 或 {"status": "error", "cmd": "seek", "reason": "..."}。错误总是附带一个用平实语言写成的原因,绝不会等到您的超时。没有请求 ID:应答通过其 "cmd" 与请求对应。
  • 播放器发出的事件({"event": ...})在同一连接上到达,与应答混杂在一起(见下文)。
  • 一次只能有一个客户端。 第二个连接要等第一个关闭后才能进行。
  • 仅限本地。 该端口只在环回上监听。看起来像浏览器请求的连接(HTTP 行、Host: 头)会被直接关闭,不执行任何内容:网页无法操控播放器。一行最多 16 MiB。
  • 通过这个端口不会传输任何机密信息用于 tracker:携带 tracker 密码、密钥或令牌的请求会被拒绝。

无需编写代码就能试用:Window > Console(Ctrl+Shift+C)可以在播放器内部发送同样的命令,并支持 Tab 补全:status、seek {"frame": 24},或完整的 JSON 形式。在执行写文件、发布或关闭播放器的命令之前,它会先询问。

Console,显示一条 seek 命令及其应答,随后是 status 应答的开头部分。

Python 客户端

bernard_api.py 用一个类 BernardClient 封装了该协议,每个命令对应一个方法。它安装在播放器旁边的 api 文件夹中:

系统位置
WindowsC:\Program Files\Bernard Review\bin\api\bernard_api.py
Linux (RPM)/usr/share/bernardplayer/api/bernard_api.py
Linux(.sh 归档)<install folder>/share/bernardplayer/api/bernard_api.py

它只需要 Python 3 的标准库。

import sys
sys.path.append(r"C:\Program Files\Bernard Review\bin\api")
from bernard_api import BernardClient

client = BernardClient(host="127.0.0.1", port=5556)
if not client.connect():
    raise SystemExit("Bernard Review does not answer on port 5556")

reply = client.open_media("D:/shots/SQ010_SH010_ANIM_v002.mov")
if reply is None or reply.get("status") == "error":
    print("refused:", reply and reply.get("reason"))
else:
    client.play()
  • request(dict) 发送一个命令并等待它的应答(一个 dict),包括错误:对于播放器的拒绝,它不会抛出异常;超时或连接丢失时返回 None。
  • send_command(dict) 发送后不等待。
  • 具名方法(play()、seek()、set_exposure()、export() 等)调用其中之一;返回数据的方法通过 request()。
  • on("frame_changed", function) 为一个事件订阅一个函数;由后台线程读取连接。

不使用客户端,几行代码就够了:

import json, socket

with socket.create_connection(("127.0.0.1", 5556)) as s:
    f = s.makefile("rw", encoding="utf-8", newline="\n")
    f.write(json.dumps({"cmd": "status"}) + "\n"); f.flush()
    for line in f:
        msg = json.loads(line)
        if msg.get("cmd") == "status":      # skip the events
            print(msg["playback"]["currentFrame"]); break

编写脚本前需要了解的事项

  • 帧号。 seek、set-in、set-out 和 --frame 选项使用的是片段中的位置,从 0 开始计数,而不是标尺上打印的编号。在从 1001 开始的片段上,显示的帧 1010 对应位置 9。status 给出位置(playback.currentFrame)和第一帧的编号(playback.startOffset)。
  • 哪些是异步的。 open 在探测片段之前就返回应答:status.playback.transitioning 一直为 true,直到片段显示在画面上。export 把任务加入队列:用 export-status 或 export-done 事件来跟踪。export-notes 在后台写入文件。set-proxy 之后,请等待 status.viewport.retiering 回到 false。
  • 全新的配置文件会在模态窗口中询问首次启动的问题:请先发送 {"cmd": "set-ui-level", "value": "complete"}。
  • quit 先应答,然后干净地关闭播放器,不会询问是否保存:如果审阅内容重要,请先发送 save-playlist 或 export-notes。
  • 多个同名请求同时在途时无法区分:请逐个发送。

按系列划分的命令

播放器的 115 个命令。括号中的字段是主要字段;track 为 "A"(默认)或 "B"。

播放与范围

命令作用
play、pause、reverse-play播放、暂停、倒放
seek (frame)跳到某个位置(从 0 开始)
step-frame (delta)向前或向后步进 delta 帧
toggle-loop循环该范围
set-playlist-playback (loop_mode 0、1 或 2,dont_wait_incomplete_shots)关闭循环、循环片段或循环播放列表
set-in、set-out (frame,可选)入点和出点;不带 frame 时使用播放头
clear-in-out、set-in-out-lock (value)清除范围;禁止用鼠标移动它
set-fps (value)、set-start (value)帧率;第一帧的编号
set-volume (value 0 到 1)、set-mute (value)、toggle-mute声音

片段与播放列表

命令作用
open 或 load (path)在轨道 A 上打开文件或序列,并添加到播放列表
load-playlist (path)载入 .bpl、.otio、.csv、.json 或 .edl 播放列表
save-playlist (path)把播放列表写为 .bpl(备注、状态、调色、绘制内容)
playlist-add (path)、playlist-play (index)、playlist-remove (index)、playlist-move (from, to)、playlist-clear管理播放列表(索引从 0 开始)
drop (paths,at:track_a、track_b 或 viewport_b)等同于从文件管理器拖入文件

对比与版本

命令作用
set-ab-mode (value)、toggle-abA/B 模式
set-track-b (path)把文件放到轨道 B 上
set-ab-view (value:a、wipe、b)、ab-flip只看 A、擦除对比、只看 B;交换 A/B
set-wipe (position 0 到 1,angle 单位为度)擦除对比的位置和角度
cycle-b (direction:prev、next)B 上的上一个或下一个版本
pin-track-b (value)、set-b-version-preload (value)固定 B;预加载相邻版本
set-ab-label-mode (interaction、always、never)、set-metadata-track (a、b、ab)A/B 字母;Metadata 选项卡显示的轨道
build-matrix (sequence, id, shots)、get-matrix填充和读取 Version Board(参见集成)

色彩

命令作用
set-exposure、set-gamma (value, track)曝光(单位为档)、伽马
set-white-balance (temp,tint 取值 -1 到 1,track)、set-offset (value:[r, g, b],track)白平衡、偏移
set-colorspace (value)轨道 A 的输入空间:ACEScg、sRGB、AP0、AdobeRGB、ProPhoto、Rec709、LogC3、LogC4,或 Auto
set-channel (value:RGBA、R、G、B、A、L;track)显示的通道
set-stage (stage, on, track)关闭或开启某个环节(exposure、levels、curves、gamma 等)
set-curve (channel:master、r、g、b;points 或 reset)曲线,ACEScct 轴上的 [x, y] 点
set-hue-range (range, hue, saturation, lightness)色相范围
color-panel (target, before_after, picker, levels_auto, preset_save, preset_apply...)色彩面板;不带任何字段时读取它
scopes (open, kind)示波器及其最近的测量值(直方图、裁切)
set-layer (track_idx, layer_idx)、layer-info (track)多图层 EXR 的通道

查看器、界面和窗口

命令作用
zoom (value,1 = 每个源像素对应一个屏幕像素)、pan (x, y)、fit、flip取景
set-fullscreen (value)、set-window-size (width, height)、set-ui-scale (percent)主窗口
set-theme (dark、light、neon、system;或 sombre、clair、systeme)、set-ui-level (essential、complete)显示模式和界面级别
reset-layout、new-window (view)、close-window (name 或 all)、windows布局和面板副本
contact-sheet (open, mode, card_w...)联系表
set-tool (tool:pen、text、arrow、rect、laser、eraser、pipette、select)绘图工具
debug-mouse (x, y, down, alt, release)模拟鼠标,用于界面测试

备注、截图与导出

命令作用
export-notes (output_dir)不弹出任何对话框,直接写出 export.json 和 export.html
screenshot (output, frame, annotations)轨道 A 的片段的 PNG,与屏幕上一样已调色
export (output, preset, source, playlist, format, codec, color, burn_in, annotations, watermark, recipient...)导出帧或视频;加入队列
export-status、export-cancel、export-presets跟踪、取消、列出预设
export-recipe (action:list 或 run;name、file 或 recipe;clips, recipient)导出方案

内存与解码

命令作用
set-proxy (proxy_idx:0 = 1/1,1 = 1/2,2 = 1/4,3 = 1/8,4 = Smart RAM-Fit)画面上的片段的解码分辨率
set-slow-decode-tier (auto, off)解码跟不上时
set-gpu-decode (value)NOVA 的 GPU 解码(beta):开、关,或查询状态
set-gpu-yuv (value)视频的 GPU YUV 上传(beta):开、关,或查询状态
set-gpu-video-decode (value)GPU 视频解码(beta):开、关,或查询状态
set-gpu-prores (value)GPU ProRes 解码(beta):开、关,或查询状态
set-ram-limit-gb、set-vram-ring-limit-gb (value;VRAM 为 0 表示 Auto)内存预算
set-hardware-decode (value)硬件解码
ram-fit-apply、precache-playlist (value)、full-quality (value)、cache-clip-frames (clip_id)Smart RAM-Fit、预加载
ram-info (frames)内存使用情况,逐个片段
cache-info、cache-purge (scope)压缩缓存:已存在,但在此版本中仍为空

审阅会话

命令作用
session-create (name, user_name, password,reach:network 或 this_computer)主持;应答中给出链接
session-join (address, user_name, password)、session-leave加入、离开
session-status完整的会话状态:参与者、主控者、语音等
session-pass-baton (participant_id)、session-set-sync (locked)移交控制权;Local Review
chat-send (text)、chat-log (last)聊天
laser (x, y, active)、laser-status激光笔
voice-test (seconds)通过语音扬声器播放一个提示音
stream-enable (on, width, height)、stream-status向网页来宾串流

Tracker(任何请求都不含机密信息)

命令作用
tracker-sitestrackers.json 中的站点
tracker-connect (site)、tracker-disconnect (site, forget)连接
tracker-projects、tracker-playlists (project_id)浏览
tracker-load-playlist (playlist_id,mode:replace 或 append)载入播放列表
tracker-publish (items, status, dry_run)发布备注
tracker-download-media (index)、tracker-cancel下载预览;取消

诊断

命令作用
status播放器的完整状态(见下文)
system-info版本、系统、显卡、库;不含姓名,不含路径
dump-thumbnails (output, track)把时间线的缩略图输出为一张 PNG 图
quit干净地关闭播放器

事件

播放器会主动向其客户端广播这些行:

事件字段
play、pause—
frame_changedframe:位置,每当它改变就发出(播放、拖动、跳转)
export-doneok, cancelled, error, frames_written, output, jobs, seconds, encoder
tracker-connectedok, site, user, reason
tracker-projects、tracker-playlistsok,projects 或 playlists,reason
tracker-loadedok, items, missing_media, reason
tracker-publish-donepublished, skipped, failed, dry_run
tracker-download-doneindex, ok,path 或 reason

status

{"cmd": "status"} 返回完整状态。最有用的块:playback(state、currentFrame、startOffset、fps、ab_mode、ab_view、transitioning,以及 late_frames 和 lost_frames 计数器)、color(轨道 A 的调色、通道)、viewport(解码分辨率和原始分辨率、proxy_idx、retiering、zoom)、decode(解码时间、too_slow)、track_b、ram_fit 和 playlist(每个片段一项)。

示例

打开最新的渲染并播放(例如从 Nuke 的按钮):

client.open_media("D:/renders/SQ010_SH010_COMP_v003/SQ010_SH010_COMP_v003.1001.exr")
client.set_colorspace("ACEScg")
client.play()

跟随播放头(例如让 Maya 的时间线保持同步):

client.on("frame_changed", lambda e: print("position", e["frame"]))
client.on("play", lambda e: print("playing"))

导出样片并等待完成:

client.export("D:/dailies/<clip>.mov", preset="dailies_prores_lt",
              playlist=True, burn_in=True)
result = client.wait_for_export()
print(result["state"], result.get("error"))

收集一次审阅的备注:

import json, os, time
out = "D:/review/SQ010_notes"
client.export_notes(out)
time.sleep(2)  # the files are written in the background
notes = json.load(open(os.path.join(out, "export.json"), encoding="utf-8"))

开始一个受密码保护的审阅会话:

r = client.session_create(name="SQ010 dailies", password="...")
for link in r["links"]:
    print(link["kind"], link["link"])   # lan, vpn...

文件格式

api 文件夹中包含所交换文件的 JSON schema:playlist.schema.json(.bpl 播放列表,带有 tracker 标识符的 x_tracking 块)、notes_payload.schema.json(备注的 export.json)和 export_recipe.schema.json(导出方案),以及 export_examples/ 中的导出任务示例。

另请参见:命令行与无界面导出、集成。