How to consume the MediaIO framework as an application author — opening backends, reading and writing frames, wiring transfers, handling cancellation, and observing state through signals.
For an introduction aimed at backend authors (writing a new MediaIO subclass), see the MediaIO Backend Guide.
MediaIO is the abstract entry point for every media source, sink, and transform — files, image sequences, capture cards, codecs, and synthetic generators. Every public method is asynchronous; every operation returns a MediaIORequest. A backend's per-stream input/output points are exposed as ports (MediaIOSource, MediaIOSink) grouped under port groups (MediaIOPortGroup). Transfers between MediaIOs are wired up with MediaIOPortConnection.
The framework contracts you should care about as a user:
MediaIO (mediaDesc(), frameRate(), …) is updated before signals fire and before request promises resolve, so a .then() callback always observes the post-update state.executeCmd once it has started.Three convenience helpers and one general factory:
Backends are discovered through the MediaIOFactory registry. MediaIOFactory::registeredFactories() returns every known factory; findByName / findByScheme / findForPath are the canonical lookups.
MediaIORequest is the unified return type for every operation that goes through the dispatch pipeline. It is a small handle to shared state; copying or destroying it does not affect the underlying request.
The four common consumption patterns:
For most consumers, the cached accessors on MediaIO / MediaIOPortGroup (described below) are easier than reading typed output fields directly — the framework copies the relevant outputs into the cache as part of completeCommand.
Async close: pass nothing to close() and use .then() for notification, or attach to the closed(Error) signal:
While closing, new readFrame / writeFrame / seekToFrame / sendParams calls return Error::NotOpen immediately. Reads already in flight resolve normally, and exactly one synthetic read result with Error::EndOfFile is appended to each source's read cache so signal-driven consumers see one trailing frameReady carrying EOS.
A backend's open returns one or more port groups; each group contains zero or more sources (MediaIOSource) and zero or more sinks (MediaIOSink).
Sources prefetch reads through MediaIOReadCache so steady-state reads complete from cache without round-tripping to the backend. Tune the prefetch depth via the source's accessors if a backend has bursty latency; the default is whatever the backend declared during Open.
The signal-driven equivalent:
Async write errors surface through the writeError(Error) signal on the sink (accessed as snk->writeErrorSignal). The sink's frameWanted signal (accessed as snk->frameWantedSignal) fires when the sink has capacity and is ready to consume more frames.
Seeking is a port-group operation since it changes the navigation state of every port in the group:
MediaIO_SeekDefault resolves to whatever mode the backend preferred at open time (exact for sample-accurate sources, keyframe-before for compressed video, etc.).
grp->setStep(2) lets you skip every other frame, or grp->setStep(-1) to play backwards. The argument is a plain int — positive forwards, zero holds, negative reverses.
MediaIO exposes cached descriptors that the framework keeps fresh:
mediaDesc(), audioDesc(), metadata(), frameRate()defaultSeekMode(), isOpen(), isClosing()MediaIOPortGroup:
currentFrame(), frameCount(), canSeek()framesDropped(), framesRepeated(), framesLate() (atomic counters)frameRate()Signals declared with PROMEKI_SIGNAL(name, ...) are accessed via the nameSignal member (e.g. closedSignal.connect(...)). The signature columns below show the declared name and argument types.
Per-MediaIO signals (on MediaIO):
closed(Error) — async close completederrorOccurred(Error) — generic errordescriptorChanged() — mid-stream MediaDesc updatePer-source signals (on MediaIOSource):
frameReady() — a result landed in the read cachePer-sink signals (on MediaIOSink):
frameWanted() — sink has capacity, ready for morewriteError(Error) — async write failedPer-connection signals (on MediaIOPortConnection — see Wiring transfers):
upstreamDone() — source reported end-of-streamerrorOccurred(Error) — non-recoverable source-side errorsinkLimitReached(MediaIOSink *) — sink hit its frame capsinkError(MediaIOSink *, Error) — non-recoverable per-sink errorallSinksDone() — every attached sink has stoppedstopped() — connection's stop() ranMediaIOPortGroup itself emits no signals — group-level state (current frame, frame count, dropped / repeated counts) is read from accessors on demand.
Read these and connect to them on the consumer thread. Cross- thread connections are auto-marshalled by the signal/slot system.
MediaIOPortConnection glues a source to one or more sinks. The common shapes:
Per-sink frame caps go through the optional second argument to addSink:
The cap says "stop after N frames" and is honoured at the next Frame::isSafeCutPoint on or after the limit-th write, so the GOP / audio packet boundary containing the cap stays complete. When the cap fires, that sink emits sinkLimitReached and stops consuming; pumping continues for the remaining sinks. When every sink has stopped (limit, error, or removed), the connection emits allSinksDone. When the source itself reports trailing EOS, the connection emits upstreamDone once. Empty / unknown FrameCount (the default) disables the cap.
MediaIORequest::cancel() is best-effort with three states:
Error::Cancelled. Reliable.executeCmd call returns whichever error its interrupted syscall produced.Use cancel() to abandon a request you no longer want; do not treat it as a guaranteed pre-empt. To race-abort, hold the request and call cancel from a different thread:
For backend-specific RPCs (set device gain, query temperature, trigger a one-shot, …), use sendParams. The name and the MediaIOParams payload are entirely backend-defined:
Backends that don't recognize name return Error::NotSupported.
Two flavors of statistics:
MediaIORequest::stats() carries QueueWaitDuration (submit→dispatch) and ExecuteDuration (the executeCmd call). Backends may add backend-specific keys.io->stats() returns a request whose stats() carries instance-wide counters (frames delivered, error counts, throughput, etc.). The stats command is urgent-flagged so polling does not block behind real I/O.For graphs of more than a couple of MediaIOs, see MediaPipeline — it wraps the MediaIO surface with a planner that builds the right port connections, inserts auto-bridges for format mismatches, and exposes a unified lifecycle (run, pause, close). For one-off transfers, MediaIOPortConnection is sufficient.
| Backend | Direction | Purpose |
|---|---|---|
TPG | source | Synthetic test pattern (color bars + audio with embedded LTC + timecode + image data band). Defaults are tuned to feed the Inspector. |
Inspector | sink | Frame validator/monitor — decodes the TPG image data band, tracks A/V sync, emits per-frame events and a periodic summary. See the Inspector user guide. |
ImageFile | both | Single image or numbered image sequence (DPX, Cineon, PNG, JPEG, JPEG XS, TGA, SGI, PNM, raw YUV). |
AudioFile | both | libsndfile-backed audio file (WAV, FLAC, AIFF, …). |
QuickTime | both | QuickTime/ISO-BMFF reader and writer. Compressed video samples ride as Annex-B bytes on the frame payload. |
Rtp | both | RTP transport for video/audio/metadata streams (SDP-driven; SMPTE 2110 / AES67 friendly). |
MjpegStream | sink | HTTP MJPEG multipart-streaming sink. |
V4L2 | source | Video4Linux2 capture device. |
CSC | transform | Color-space / pixel-format converter. |
SRC | transform | Audio sample-rate converter. |
Burn | transform | Burn-in overlay generator (text/timecode) for QA. |
FrameSync | transform | Frame-rate sync / re-clock. |
VideoEncoder | transform | Generic video encoder (dispatches to the registered codec). |
VideoDecoder | transform | Generic video decoder (codec auto-detected from the compressed payload). |
FrameBridge | transform | Frame format adapter for stitching mismatched ports. |
RawBitstream | sink | Writes the compressed payload of every frame verbatim — the elementary-stream mirror of the encoder output. |
NullPacing | transform | Drop-everything pacing stage for soak testing. |
DebugMedia | both | Diagnostic media file format for offline inspection. |
The TPG and Inspector are designed to be paired: a default-config TPG produces everything a default-config Inspector knows how to verify (image data band, BCD timecode, LTC audio, A/V sync), so a smoke-test pipeline is two factory calls plus a connection.