ENTWICKLERDOKUMENTATION

Python-API und IPC

Bernard Review aus einem Skript oder einer Pipeline-Anwendung steuern. Das Protokoll, der Python-Client, jeder Befehl nach Familie, die Ereignisse und vollständige Beispiele.

Bernard Review lässt sich aus Python, Maya, Nuke, Houdini, einem Produktions-Tracker oder jeder Sprache steuern, die eine TCP-Verbindung öffnen kann. Alles, was ein Artist von Hand tut, hat seinen Befehl: Clips öffnen, abspielen, vergleichen, graden, exportieren, eine Sitzung starten, Notizen veröffentlichen. Diese Seite genügt, um den Player zu automatisieren; die erschöpfende Referenz, Feld für Feld, ist die API-Referenz, auf Englisch (auch über Help > API Documentation zu öffnen).

Das Protokoll

  • Adresse: 127.0.0.1, standardmäßig Port 5556. Ein mit --ipc-port N gestarteter Player lauscht auf N: So laufen zwei Player auf demselben Rechner.
  • Format: JSON Lines. Jede Anfrage ist ein JSON-Objekt in einer Zeile, beendet mit \n, mit einem Feld "cmd": {"cmd": "seek", "frame": 24}.
  • Eine Antwort pro Anfrage, in der gesendeten Reihenfolge, meist innerhalb weniger Millisekunden: {"status": "ok", "cmd": "seek", ...} oder {"status": "error", "cmd": "seek", "reason": "..."}. Ein Fehler trägt immer einen Grund in einfachen Worten und wartet nie auf Ihr Zeitlimit. Es gibt keine Anfrage-ID: Eine Antwort wird ihrer Anfrage über ihr "cmd" zugeordnet.
  • Ereignisse des Players ({"event": ...}) kommen auf derselben Verbindung an, gemischt mit den Antworten (siehe unten).
  • Immer nur ein Client. Eine zweite Verbindung wartet, bis die erste geschlossen wird.
  • Nur lokal. Der Port lauscht auf dem Loopback. Eine Verbindung, die wie die Anfrage eines Browsers aussieht (eine HTTP-Zeile, ein Header Host:), wird geschlossen, ohne etwas auszuführen: Eine Webseite kann den Player nicht steuern. Eine Zeile umfasst höchstens 16 MiB.
  • Kein Secret geht für Tracker über diesen Port: Eine Anfrage, die ein Passwort, einen Schlüssel oder ein Token eines Trackers enthält, wird abgelehnt.

Zum Ausprobieren ohne Code: Window > Console (Strg+Umschalt+C) sendet dieselben Befehle aus dem Player heraus, mit Vervollständigung per Tab: status, seek {"frame": 24} oder die vollständige JSON-Form. Sie fragt vor den Befehlen nach, die Dateien schreiben, veröffentlichen oder den Player schließen.

Die Console mit einem Befehl seek und seiner Antwort, dann dem Anfang der Antwort auf status.

Der Python-Client

bernard_api.py verpackt das Protokoll in eine Klasse, BernardClient, mit einer Methode pro Befehl. Er wird neben dem Player im Ordner api installiert:

SystemOrt
WindowsC:\Program Files\Bernard Review\bin\api\bernard_api.py
Linux (RPM)/usr/share/bernardplayer/api/bernard_api.py
Linux (.sh-Archiv)<install folder>/share/bernardplayer/api/bernard_api.py

Er braucht nichts außer der Standardbibliothek von 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) sendet einen Befehl und wartet auf seine Antwort (ein dict), Fehler eingeschlossen: Bei einer Ablehnung durch den Player löst es keine Exception aus und liefert bei einem Zeitlimit oder einer verlorenen Verbindung None.
  • send_command(dict) sendet, ohne zu warten.
  • Die benannten Methoden (play(), seek(), set_exposure(), export() ...) rufen die eine oder die andere auf; die, die Daten liefern, laufen über request().
  • on("frame_changed", function) meldet eine Funktion für ein Ereignis an; ein Hintergrund-Thread liest die Verbindung.

Ohne den Client genügen ein paar Zeilen:

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

Was Sie wissen sollten, bevor Sie ein Skript schreiben

  • Bildnummern. seek, set-in, set-out und die Option --frame nehmen die Position im Clip ab 0 gezählt, nicht die auf dem Lineal gedruckte Nummer. Bei einem Clip, der bei 1001 beginnt, ist das angezeigte Bild 1010 die Position 9. status liefert die Position (playback.currentFrame) und die erste Bildnummer (playback.startOffset).
  • Was asynchron ist. open antwortet, bevor der Clip untersucht ist: status.playback.transitioning bleibt true, bis der Clip auf dem Bildschirm ist. export stellt einen Auftrag in die Warteschlange: Verfolgen Sie ihn mit export-status oder dem Ereignis export-done. export-notes schreibt seine Dateien im Hintergrund. Warten Sie nach set-proxy, bis status.viewport.retiering wieder auf false steht.
  • Ein frisches Profil stellt die Frage des ersten Starts in einem modalen Fenster: Senden Sie zuerst {"cmd": "set-ui-level", "value": "complete"}.
  • quit antwortet und schließt dann den Player sauber, ohne zu fragen, ob gespeichert werden soll: Senden Sie zuerst save-playlist oder export-notes, wenn das Review wichtig ist.
  • Mehrere gleichnamige Anfragen gleichzeitig lassen sich nicht unterscheiden: Senden Sie sie nacheinander.

Befehle nach Familie

Die 115 Befehle des Players. Die Felder in Klammern sind die wichtigsten; track ist "A" (Standard) oder "B".

Wiedergabe und Bereich

BefehlWirkung
play, pause, reverse-playAbspielen, Pause, rückwärts abspielen
seek (frame)Zu einer Position gehen (ab 0)
step-frame (delta)delta Bilder vor oder zurück gehen
toggle-loopDen Bereich wiederholen
set-playlist-playback (loop_mode 0, 1 oder 2, dont_wait_incomplete_shots)Wiederholung aus, auf dem Clip oder auf der Playlist
set-in, set-out (frame, optional)In- und Out-Punkt; ohne frame der Playhead
clear-in-out, set-in-out-lock (value)Den Bereich löschen; verhindern, dass die Maus ihn verschiebt
set-fps (value), set-start (value)Bildrate; erste Bildnummer
set-volume (value 0 bis 1), set-mute (value), toggle-muteTon

Clips und Playlist

BefehlWirkung
open oder load (path)Eine Datei oder Sequenz auf Spur A öffnen und der Playlist hinzufügen
load-playlist (path)Eine Playlist als .bpl, .otio, .csv, .json oder .edl laden
save-playlist (path)Die Playlist als .bpl schreiben (Notizen, Status, Grading, Zeichnungen)
playlist-add (path), playlist-play (index), playlist-remove (index), playlist-move (from, to), playlist-clearDie Playlist verwalten (Indizes ab 0)
drop (paths, at: track_a, track_b oder viewport_b)Wie das Ablegen von Dateien aus dem Dateimanager

Vergleich und Versionen

BefehlWirkung
set-ab-mode (value), toggle-abA/B-Modus
set-track-b (path)Eine Datei auf Spur B legen
set-ab-view (value: a, wipe, b), ab-flipNur A, Wipe, nur B; A/B umschalten
set-wipe (position 0 bis 1, angle in Grad)Position und Winkel des Wipes
cycle-b (direction: prev, next)Vorige oder nächste Version auf B
pin-track-b (value), set-b-version-preload (value)B anheften; die benachbarten Versionen vorladen
set-ab-label-mode (interaction, always, never), set-metadata-track (a, b, ab)A/B-Buchstaben; die Spur des Tabs Metadata
build-matrix (sequence, id, shots), get-matrixDas Version Board füllen und lesen (siehe Integrationen)

Farbe

BefehlWirkung
set-exposure, set-gamma (value, track)Belichtung (in Blenden), Gamma
set-white-balance (temp, tint von -1 bis 1, track), set-offset (value: [r, g, b], track)Weißabgleich, Offset
set-colorspace (value)Eingangsraum von Spur A: ACEScg, sRGB, AP0, AdobeRGB, ProPhoto, Rec709, LogC3, LogC4 oder Auto
set-channel (value: RGBA, R, G, B, A, L; track)Angezeigter Kanal
set-stage (stage, on, track)Eine Stufe aus- oder einschalten (exposure, levels, curves, gamma ...)
set-curve (channel: master, r, g, b; points oder reset)Kurven, Punkte [x, y] auf der ACEScct-Achse
set-hue-range (range, hue, saturation, lightness)Farbbereiche
color-panel (target, before_after, picker, levels_auto, preset_save, preset_apply ...)Das Farb-Panel; ohne Feld liest es es aus
scopes (open, kind)Die Scopes und ihre letzten Messwerte (Histogramm, Clipping)
set-layer (track_idx, layer_idx), layer-info (track)Pass eines Multi-Layer-EXR

Viewer, Oberfläche und Fenster

BefehlWirkung
zoom (value, 1 = ein Quellpixel pro Bildschirmpixel), pan (x, y), fit, flipBildausschnitt
set-fullscreen (value), set-window-size (width, height), set-ui-scale (percent)Hauptfenster
set-theme (dark, light, neon, system; oder sombre, clair, systeme), set-ui-level (essential, complete)Anzeigemodus und Oberflächenstufe
reset-layout, new-window (view), close-window (name oder all), windowsLayout und Panel-Kopien
contact-sheet (open, mode, card_w ...)Kontaktbogen
set-tool (tool: pen, text, arrow, rect, laser, eraser, pipette, select)Zeichenwerkzeug
debug-mouse (x, y, down, alt, release)Simulierte Maus, für Oberflächentests

Notizen, Aufnahmen und Export

BefehlWirkung
export-notes (output_dir)export.json und export.html ohne jeden Dialog schreiben
screenshot (output, frame, annotations)Ein PNG des Clips von Spur A, gegradet wie auf dem Bildschirm
export (output, preset, source, playlist, format, codec, color, burn_in, annotations, watermark, recipient ...)Einzelbilder oder einen Film exportieren; in die Warteschlange gestellt
export-status, export-cancel, export-presetsVerfolgen, abbrechen, die Presets auflisten
export-recipe (action: list oder run; name, file oder recipe; clips, recipient)Export-Rezepte

Speicher und Decodierung

BefehlWirkung
set-proxy (proxy_idx: 0 = 1/1, 1 = 1/2, 2 = 1/4, 3 = 1/8, 4 = Smart RAM-Fit)Decodierauflösung des Clips auf dem Bildschirm
set-slow-decode-tier (auto, off)Wenn die Decodierung nicht mithält
set-gpu-decode (value)GPU-Decodierung für NOVA (Beta): an, aus oder der Zustand
set-gpu-yuv (value)GPU-YUV-Upload für Filme (Beta): an, aus oder der Zustand
set-gpu-video-decode (value)GPU-Videodecodierung (Beta): an, aus oder der Zustand
set-gpu-prores (value)GPU-ProRes-Decodierung (Beta): an, aus oder der Zustand
set-ram-limit-gb, set-vram-ring-limit-gb (value; 0 = Auto für VRAM)Speicherbudgets
set-hardware-decode (value)Hardware-Decodierung
ram-fit-apply, precache-playlist (value), full-quality (value), cache-clip-frames (clip_id)Smart RAM-Fit, Vorladen
ram-info (frames)Speichernutzung, Clip für Clip
cache-info, cache-purge (scope)Kompressions-Cache: vorhanden, in dieser Version aber noch leer

Review-Sitzungen

BefehlWirkung
session-create (name, user_name, password, reach: network oder this_computer)Leiten; die Antwort liefert die Links
session-join (address, user_name, password), session-leaveBeitreten, verlassen
session-statusDer gesamte Sitzungszustand: Teilnehmer, Pilot, Stimme ...
session-pass-baton (participant_id), session-set-sync (locked)Kontrolle weitergeben; Local Review
chat-send (text), chat-log (last)Chat
laser (x, y, active), laser-statusLaserpointer
voice-test (seconds)Einen Ton über den Sprachlautsprecher abspielen
stream-enable (on, width, height), stream-statusStreaming zu Web-Gästen

Tracker (kein Secret in irgendeiner Anfrage)

BefehlWirkung
tracker-sitesDie Sites aus trackers.json
tracker-connect (site), tracker-disconnect (site, forget)Verbindung
tracker-projects, tracker-playlists (project_id)Durchsuchen
tracker-load-playlist (playlist_id, mode: replace oder append)Eine Playlist laden
tracker-publish (items, status, dry_run)Die Notizen veröffentlichen
tracker-download-media (index), tracker-cancelEine Vorschau herunterladen; abbrechen

Diagnose

BefehlWirkung
statusDer gesamte Zustand des Players (siehe unten)
system-infoVersion, System, Grafikkarte, Bibliotheken; kein Name, kein Pfad
dump-thumbnails (output, track)Die Vorschaubilder der Timeline als PNG-Bogen
quitDen Player sauber schließen

Ereignisse

Der Player sendet diese Zeilen ungefragt an seinen Client:

EreignisFelder
play, pause—
frame_changedframe: die Position, bei jedem Bild, in dem sie sich ändert (Wiedergabe, Ziehen, Sprung)
export-doneok, cancelled, error, frames_written, output, jobs, seconds, encoder
tracker-connectedok, site, user, reason
tracker-projects, tracker-playlistsok, projects oder playlists, reason
tracker-loadedok, items, missing_media, reason
tracker-publish-donepublished, skipped, failed, dry_run
tracker-download-doneindex, ok, path oder reason

status

{"cmd": "status"} liefert den gesamten Zustand. Die nützlichsten Blöcke: playback (state, currentFrame, startOffset, fps, ab_mode, ab_view, transitioning, die Zähler late_frames und lost_frames), color (Grading von Spur A, Kanal), viewport (decodierte und originale Auflösung, proxy_idx, retiering, zoom), decode (Decodierzeit, too_slow), track_b, ram_fit und playlist (ein Eintrag pro Clip).

Beispiele

Den neuesten Render öffnen und abspielen (zum Beispiel aus einer Nuke-Schaltfläche):

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

Dem Playhead folgen (zum Beispiel, um eine Maya-Timeline im Gleichschritt zu halten):

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

Dailies exportieren und auf das Ende warten:

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"))

Die Notizen eines Reviews einsammeln:

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"))

Eine passwortgeschützte Review-Sitzung starten:

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

Dateiformate

Der Ordner api enthält die JSON-Schemas der ausgetauschten Dateien: playlist.schema.json (die .bpl-Playlist, mit dem Block x_tracking der Tracker-Kennungen), notes_payload.schema.json (das export.json der Notizen) und export_recipe.schema.json (Export-Rezepte) sowie Beispiel-Exportaufträge in export_examples/.

Siehe auch: Kommandozeile und Export ohne Fenster, Integrationen.