Skip to content
Haiku OSDeep Dive Published Updated 7 min readViews unavailable

Haiku BMediaEncoder: Codec Output, Chunk Writing, and Container Boundaries

Encode Haiku media with BMediaEncoder by selecting a codec, negotiating input, validating parameters, writing chunks, and separating muxing.

BMediaEncoder wraps a Media Kit encoder add-on and calls application code when encoded chunks are ready to be written. It is a codec interface, not a complete export pipeline. It does not automatically choose a container, write a file header, interleave audio and video tracks, or make an incomplete output file valid. A production encoder path must coordinate format negotiation, chunk writes, timestamps, buffering, finalization, and cancellation explicitly.

Select an encoder and check status

The class can be initialized from an output media_format or from a media_codec_info record. SetTo() releases any previous encoder and asks the plugin manager for a suitable implementation. InitCheck() reports whether the current encoder is ready. Check every result before calling SetFormat(), Encode(), or parameter methods; a default-constructed object begins uninitialized.

Choosing by format lets the plugin manager locate an encoder that claims the requested encoded format. Choosing by codec info pins selection more directly. Neither path proves the codec can encode every input format or every dimension/sample rate. The subsequent input-format setup is a separate compatibility decision and can fail.

Format structures can include wildcards while a node is negotiating capabilities. Do not pass an unresolved format to the data path and then assume the encoder will pick anything the application wants. Ask for the desired output, inspect the format returned by setup, and present the effective properties to the user. Verify bit rate, frame size, sample rate, channel layout, color space, dimensions, and codec profile only when the selected codec documents those fields.

Set up the input side

SetFormat() receives in/out pointers for input and output media_format and an optional media_file_format. In current upstream implementation, an output format triggers SetTo() and the input is passed to the encoder’s setup routine. The implementation includes a TODO for support of the file-format argument, so do not rely on that parameter to configure a muxer or create a container. Treat it as unsupported unless the exact target release documents otherwise.

After setup, retain the actual input and output formats used. The input buffer passed to Encode() must match that input format, and frameCount must describe the number of input frames for that call. It is not a byte count. Calculate input bytes from the format with checked arithmetic, and avoid passing a pointer to fewer bytes than a full frame requires.

For streaming input, keep a clear distinction between frames accumulated from the source and chunks produced by the codec. An encoder may buffer input before it can emit output. Therefore, one Encode() call does not necessarily equal one complete output packet or one container sample. Preserve timestamps and codec metadata from media_encode_info as required by the API and selected codec.

Write chunks through the subclass hook

BMediaEncoder is abstract because subclasses implement WriteChunk(). The wrapper routes encoded output into this method. The hook receives the encoded bytes, byte size, and media_encode_info; it is the application boundary for passing chunks to a muxer or sink. Keep it short and report failures accurately so a disk or network error does not look like successful encoding.

status_t MyEncoder::WriteChunk(const void* bytes, size_t size,
    media_encode_info* info)
{
    if (bytes == NULL || info == NULL)
        return B_BAD_VALUE;
    if (!outputQueue.TryCopy(bytes, size, *info))
        return B_WOULD_BLOCK;
    return B_OK;
}

This sketch assumes TryCopy() owns the bytes and metadata when it returns success. A queue that merely stores the pointer is unsafe unless the encoder explicitly guarantees its lifetime. Use a bounded queue and define backpressure: stop capture, fail the export, or drop only if the product explicitly allows lossy output. Do not let the hook block indefinitely on slow storage.

The output hook should not mutate the encoded data unless the container or codec specification calls for such a transformation. Keep packet ordering, timestamps, chunk flags, and track metadata intact. If the muxer needs to reorder packets or delay interleaving, design a separate muxing stage with explicit per-track queues and a bounded policy.

Codec parameters are not universal controls

GetEncodeParameters() and SetEncodeParameters() expose an encode_parameters structure for codec settings. The set of meaningful fields depends on the selected encoder. Read the structure and codec info for the active implementation; do not display a generic quality slider and claim that every codec interprets it as the same visual or audio quality. Apply a parameter change only at a boundary supported by the codec, and check the returned status.

Likewise, AddTrackInfo() passes metadata to the encoder implementation. It should not be conflated with writing a container’s arbitrary tags. Check which keys the codec supports and which metadata belongs in the muxer. A successful codec metadata call is not proof the final media file contains the tag that a playback application will display.

If parameters are user-editable, store them alongside the codec identity and versioned export profile. On reload, enumerate or query the current encoder, validate that the codec still exists, and ignore settings the replacement codec does not understand. Never reinterpret an old codec’s numeric field as a different parameter after software changes.

The buffer encoder has a narrow purpose

BMediaBufferEncoder offers EncodeToBuffer() for a caller-supplied destination buffer. It is useful when output size is known or deliberately bounded, but the caller must interpret the returned size and error result carefully. A fixed buffer can be too small for a variable-bitrate chunk; do not assume average bitrate proves the maximum output size.

When a buffer is insufficient, follow the API’s reported failure behavior and retry only if the encoder contract allows the operation to be retried without consuming input state twice. A safer design can encode into a chunk-writer subclass that allocates or queues bounded chunks. Do not reuse an output buffer while a downstream writer still references it.

Container writing and finalization

A codec encodes elementary chunks; a container defines track descriptions, timestamps, interleaving, indexes, and final metadata. Keep those roles separate. BMediaEncoder alone does not create a complete media file. If writing a file, use a documented container writer or implement the container specification, including header updates and finalization. A file that contains valid encoded packets can still be unreadable if its container index or durations are missing.

Write to a temporary destination when practical. Check every queue and file operation, flush and close the writer, finalize the container, and only then publish the result. If cancellation arrives, stop accepting new input, drain or discard queued chunks according to policy, ask the encoder to finish only through a supported API, then leave a clearly failed or removed partial file. Do not rename a half-written output to the final user-visible path.

For audio/video exports, keep per-track timing in the media-time domain and define how discontinuities are represented. Do not assume wall-clock arrival time is the correct packet timestamp. If a codec introduces delay, consult its metadata and the Media Kit’s encoding structures rather than silently shifting every frame by an arbitrary constant.

Verification checklist

Test no matching encoder, invalid input format, wildcard output negotiation, malformed frame count, empty input, output-hook failure, full bounded queue, insufficient buffer capacity, parameter rejection, cancellation during encode, and finalization failure. Compare the output with an independent decoder and verify duration, track count, timestamps, and codec identification. A successful Encode() status proves the API accepted that operation; it does not validate the resulting container or application playback.

Log codec identity, negotiated formats, input frame count, output bytes, output timestamp range, queue depth, parameter status, and final muxer status. Keep a small deterministic fixture so codec changes can be tested without involving real-time capture or hardware.

BMediaEncoder is one stage in an export pipeline. Select a real codec, negotiate concrete formats, preserve frame and timestamp semantics, own output bytes before asynchronous handoff, and implement muxing and finalization as distinct, testable stages.

Related:

Sources:

Comments