3 Overview
Upipe provides an extensive set of header files, to be used by pipes and applications. Applications will also generally want to link with optional libraries, which allow to create and manage some core data structures.
3.1 Data structures
Upipe's data structures can be classified into four groups, as shown by the following graph which depicts dependancy relationships between structures (dashed lines are optional):

Core structures
Core structures provide services for all other groups:
atomic operations with or without locks (uatomic_uint32_t)
reference counters (struct urefcount)
chained lists (struct uchain) and rings allowing pointer tagging (struct uring)
lock-less LIFOs (struct ulifo) without thread limitations
lock-less FIFOs (struct ufifo) without thread limitations
lock-less buffer queues (struct uqueue) without thread limitations
lock-less buffer pools (struct upool) without thread limitations
lock-less exclusive access to non-reentrant resources (struct udeal)
objects to pass event information between threads (struct ueventfd)
dictionaries storing key/value pairs (struct udict)
memory allocation with or without pools (struct umem)
access to monotonic or real-time system clock (struct uclock)
Buffer management
Buffer management structures define how data is carried across inside a pipeline and are handled by struct ubuf and associated managers. These structures typically point to a shared, reference counted area maintained by the manager. Upipe natively supports three types of buffers, each with a dedicated API to access and modify data :
blocks (arbitrary octet content with arbitrary and variable size), typically used for encoded data: ubuf_block_alloc ; the API supports windowing and resizing blocks, as well as merging and cutting out with zero-copy and copy-on-write semantics.
pictures, defined by their chromatic planes, pixel sizes and subsampling, with arbitrary width and height: ubuf_pic_alloc.
sounds, in planar or packed formats: ubuf_sound_alloc.
Pipes and applications however do not manipulate struct ubuf directly, but struct uref, which is composed of a pointer to struct ubuf and a pointer to struct udict. The dictionary allows to associate arbitrary attributes to the struct ubuf.
An attribute is defined by a category, a name, a type and a value. Available types are:
opaque: stores a buffer of an arbitrary length
string: stores a string terminated by \0
void: doesn't store anything but the presence or absence of the attribute (flag)
bool: stores a true or false value
small_unsigned: stores a uint8_t
small_int: stores an int8_t
unsigned: stores a uint64_t
int: stores an int64_t
rational: stores a struct urational
float: stores a double
The standard Upipe distribution provides managers for block and picture formats, relying on application memory allocation (malloc). They in turn use the struct umem_mgr facility to allocate buffer spaces.
Pipes
Pipes are provided by specialized modules, possibly delivered by third-parties. A struct upipe may expose input and control methods (with standard and custom commands), and would typically take data from its input, process it and output it, possibly using a different buffer, to its output. Functions from a pipe may only be called from a single thread, so locking and reentrancy isn't required.
When created, pipes are passed one structure for logging and sending exceptions (struct uprobe) to the parent code upon certain events (end of file, fatal error, new flow, etc.), and optional arguments, in particular, for pipes that aren't sources, a flow definition packet of type struct uref describing the input. Pipe allocation is performed with upipe_void_alloc or upipe_flow_alloc, depending whether the pipe requests a flow definition packet. A few pipes may also provide their own specific allocator.
The standard Upipe distribution currently contains the following pipe types (though third-party modules may also be used):
API name | description | link with |
---|---|---|
upipe_fsrc_mgr_alloc | source pipe opening for reading a file or special file characterized by its path | -lupipe-modules |
upipe_fsink_mgr_alloc | sink pipe opening for writing a file or special file characterized by its path | -lupipe-modules |
upipe_udpsrc_mgr_alloc | source pipe opening for reading a UDP socket | -lupipe-modules |
upipe_udpsink_mgr_alloc | sink pipe opening for writing a UDP socket | -lupipe-modules |
upipe_multicat_sink_mgr_alloc | sink pipe opening for writing a directory in a manner compatible with multicat | -lupipe-modules |
upipe_dup_mgr_alloc | split pipe allowing to duplicate all input packets to several outputs | -lupipe-modules |
upipe_idem_mgr_alloc | linear pipe outputting packets identically | -lupipe-modules |
upipe_qsrc_mgr_alloc (also note upipe_qsrc_alloc) | source pipe opening a thread-safe queue with one or more qsink pipes | -lupipe-modules |
upipe_qsink_mgr_alloc | sink pipe sending buffers to a queue opened by a qsrc pipe | -lupipe-modules |
upipe_null_mgr_alloc | sink pipe destroying all input buffers | -lupipe-modules |
upipe_xfer_mgr_alloc (also note upipe_xfer_alloc) | pipe manager allowing to attach another pipe to a given upump_mgr running in a different thread | -lupipe-modules |
upipe_wsrc_mgr_alloc (also note upipe_wsrc_alloc) | pipe manager allowing to deport a source pipe to a different thread | -lupipe-modules |
upipe_wlin_mgr_alloc (also note upipe_wlin_alloc) | pipe manager allowing to deport a linear pipe to a different thread | -lupipe-modules |
upipe_wsink_mgr_alloc (also note upipe_wsink_alloc) | pipe manager allowing to deport a sink pipe to a different thread | -lupipe-modules |
upipe_trickp_mgr_alloc | pipe facilitating trick play operations | -lupipe-modules |
upipe_setflowdef_mgr_alloc | pipe setting given attributes on incoming flow definitions | -lupipe-modules |
upipe_setattr_mgr_alloc | pipe setting given attributes on all incoming buffers | -lupipe-modules |
upipe_setrap_mgr_alloc | pipe setting attribute k.systime_rap on all incoming buffers | -lupipe-modules |
upipe_nodemux_mgr_alloc | pipe creating timestamps for single streamps | -lupipe-modules |
upipe_noclock_mgr_alloc | pipe creating system timestamps for off-line streamps | -lupipe-modules |
upipe_genaux_mgr_alloc | pipe generating multicat-style auxiliary blocks | -lupipe-modules |
upipe_multicat_sink_mgr_alloc | sink pipe generating multicat-style directories | -lupipe-modules |
upipe_avfsrc_mgr_alloc | source pipe opening for reading a URL and using libavformat | -lupipe-av |
upipe_avcdec_mgr_alloc | linear pipe decoding a video or audio flow using libavcodec | -lupipe-av |
upipe_avcenc_mgr_alloc | linear pipe encoding a video or audio flow using libavcodec | -lupipe-av |
upipe_sws_mgr_alloc | linear pipe scaling a flow of pictures using libswscale | -lupipe-sws |
upipe_sws_thumbs_mgr_alloc | linear pipe building a mosaic of thumbnails out of a picture flow | -lupipe-sws |
upipe_ts_demux_mgr_alloc | split pipe demultiplexing a TS stream (also features lots of subpipes) | -lupipe-ts |
upipe_ts_mux_mgr_alloc | join pipe multiplexing a TS stream (also features lots of subpipes) | -lupipe-ts |
upipe_mpgaf_mgr_alloc | linear pipe gathering MPEG-1 and MPEG-2 audio (including AAC ADTS) streams into frames | -lupipe-framers |
upipe_mpgvf_mgr_alloc | linear pipe gathering MPEG-1 and MPEG-2 video streams into frames | -lupipe-framers |
upipe_h264f_mgr_alloc | linear pipe gathering MPEG-4 AVC video streams into frames | -lupipe-framers |
upipe_ffmt_mgr_alloc | linear pipe transforming the format into another specified (potentially for an encoder) | -lupipe-filters |
upipe_fdec_mgr_alloc | linear pipe decoding a stream | -lupipe-filters |
upipe_fenc_mgr_alloc | linear pipe encoding a stream | -lupipe-filters |
upipe_filter_blend_mgr_alloc | linear pipe deinterlacing pictures using a blending algorithm | -lupipe-filters |
upipe_x264_mgr_alloc | linear pipe encoding to MPEG-4 AVC using libx264 | -lupipe-x264 |
upipe_glx_sink_mgr_alloc | sink pipe displaying picture on a GLX output | -lupipe-gl |
Data is fed into a pipe using upipe_input. The struct uref argument then belongs to the callee and shouldn't be used any longer. There is an additional struct upump argument that points to the pump that generated the buffer (or NULL if unavailable).
The generic upipe_control call provides the application with an interface to modify the pipe's property. The counterpart of this function is provided by struct uprobe, which allows the pipe to send messages to the application. It is possible to build upon this messaging system to dynamically take actions on the pipe or the pipeline. All parameters to upipe_control belong to the caller. All parameters sent by subpipes via probes also belong to the subpipe. The standard Upipe distribution currently contains the following catchers:
API name | description | link with |
---|---|---|
uprobe_stdio_alloc | print all log messages to a file stream | -lupipe |
uprobe_pfx_alloc | prefix all log messages with the given name | -lupipe |
uprobe_output_alloc | forward flow definition changes to the output pipe | -lupipe |
uprobe_upump_mgr_alloc | give a upump manager to pipes that throw UPROBE_NEED_UPUMP_MGR | -lupipe |
uprobe_uref_mgr_alloc | give a uref manager to pipes that throw UPROBE_NEED_UREF_MGR | -lupipe |
uprobe_uclock_alloc | give a uclock to pipes that throw UPROBE_NEED_UREF_MGR | -lupipe |
uprobe_ubuf_mem_alloc | allocated and give a ubuf manager to pipes that throw UPROBE_NEW_FLOW_FORMAT | -lupipe |
uprobe_selflow_alloc | select flows according to criteria | -lupipe |
uprobe_dejitter_alloc | dejitter packets coming from a network by averaging reference clocks | -lupipe |
uprobe_xfer_alloc | forward probes from one thread to another, in conjunction with upipe_xfer_alloc | -lupipe |
uprobe_filter_suggest_alloc | on the UPROBE_FILTER_SUGGEST_FLOW_DEF event, ask the output pipe to suggest a flow definition using upipe_suggest_flow_def | -lupipe-filters |
In Upipe's design, decision taking happens inside probes, while data processing is done in pipes with no intelligence whatsoever.
Control commands and pipes are classified (that is, enum values are prepended with a prefix) into 7 categories:
generic: commands and probes which can apply to any type of pipe (no prefix)
source: for pipes that have no input, but rely instead on external events to retrieve incoming data
join: for pipes that have several inputs, such as a mux
split: for pipes that have several outputs, such as a demux
sink: for pipes that have no output and may rely on external events
void: for pipes that have neither input nor output (such a pipe may be used internally to create other pipes)
pipe type-specific commands and probes which must be prefixed with the short name of the pipe
As a convenience, the Upipe distribution provides a number of "helper" macros which usually manage internal structures and control commands:
helper macro | description |
---|---|
UPIPE_HELPER_UPIPE | very basic helper providing the upipe_foo_from_upipe and upipe_foo_to_upipe functions on which most helpers rely |
UPIPE_HELPER_VOID | helper for pipes which require no argument to their allocation function |
UPIPE_HELPER_FLOW | helper for pipes which require a flow definition packet to be passed as argument to their allocation function |
UPIPE_HELPER_UREF_MGR | helper for pipes which require a uref manager |
UPIPE_HELPER_UPUMP_MGR | helper for pipes which require a upump manager |
UPIPE_HELPER_UPUMP | helper for pipes which rely on external events |
UPIPE_HELPER_UCLOCK | helper for pipes which requires a uclock |
UPIPE_HELPER_OUTPUT | helper for the management of the output |
UPIPE_HELPER_UBUF_MGR | helper for the management of the ubuf manager for the output |
UPIPE_HELPER_SOURCE_READ_SIZE | helper for source pipes reading data in chunks of a configuration size |
UPIPE_HELPER_SINK | helper for sink pipes outputting data at a certain rate, to block pumps and buffer urefs |
UPIPE_HELPER_UREF_STREAM | helper for pipes reading data octet by octet, for instance to constitute packets or frames |
UPIPE_HELPER_SUBPIPE | helper for split pipes and join pipes to manage their subpipes |
UPIPE_HELPER_SYNC | helper for pipes that wish to tell when the signal is acquired or lost |
UPIPE_HELPER_BIN | helper for bin pipes (that incorporate a sub-pipeline of several pipes) |
UPROBE_HELPER_UPROBE | very basic helper providing the uprobe_foo_from_uprobe and uprobe_foo_to_uprobe functions on which most helpers rely |
UPROBE_HELPER_ALLOC | helper providing easy probe allocation and deallocation |
Strictly speaking, a struct upipe object has at most one input and one output (and possibly none). Split and join pipes are implemented using subpipes: the main split (resp. join) pipe is a sink (resp. a source), and each output (resp. input) requires allocating a source (resp. sink) subpipe using upipe_void_alloc_sub or upipe_flow_alloc_sub, depending whether the subpipe requests a flow definition. In a split pipe, outputs are configured with upipe_set_output and upipe_set_flow_def called on the output subpipe; however data is fed into the main pipe. In a join pipe, data is input using upipe_input on each input subpipe; however outputs are configured on the main pipe. The caller must therefore keep a constellation of objects, not only the main pipe but also all the subpipes. Outputs (resp. inputs) are closed by calling upipe_release on the related subpipe.
External events
Source pipes and sink pipes (but not exclusively) rely on external events to retrieve or dispatch data. For instance, one may want to wait on a UDP socket for packets. Or to wait until a system pipe (mkfifo) can be written again. Or more simply, wait for a timeout.
Pipes which need those interactions can create pumps with the built-in primitives. The struct upump abstraction layer then maps the events to the API of the event loop which is used by the application. It supports the following types of events:
idlers are executed whenever there is nothing else to do: upump_alloc_idler
timers are executed after a given timeout: upump_alloc_timer
read (resp. write) file descriptor watchers are executed whenever data is ready to be read from (resp. written to) a file descriptor: upump_alloc_fd_read (resp. upump_alloc_fd_write)
It is expected that more event types get added in the future, especially for Microsoft Windows(tm)-specific objects. Some core objects, which require being able to wait on a condition, propose their own API to allocate an adequate upump (uqueue_upump_alloc_pop or udeal_upump_alloc). In turn, they rely on the struct ueventfd object provided by Upipe.
A sink pipe which can no longer write to its output may block the source pump (passed to upipe_input) using upump_blocker_alloc. A call-back must be provided, which will be called upon the destruction of the source pump.
Upipe currently provides abstraction layers for these event loops:
event loop | API | link with |
---|---|---|
libev | upump_ev_mgr_alloc | -lupump-ev -lev |
3.2 Managers
To deal with structures efficiently, Upipe has a notion of "managers", which are similar to factories in object-oriented programming. Consequently, struct ubuf are created by a struct ubuf_mgr, struct udict by struct udict_mgr, struct uref by struct uref_mgr, struct upump by struct upump_mgr and struct upipe by struct upipe_mgr. Managers typically deal with memory pools or hardware resources such as access to video memory or hardware decoding.
Upipe provides standard implementations of struct ubuf_mgr (for blocks and pictures), struct udict_mgr, struct umem_mgr and struct uref_mgr in the libupipe library (link with -lupipe).
The struct upump_mgr implementations are supplied by dedicated, API-specific libraries such as libupump-ev. Finally, struct upipe_mgr managers allow to create the pipes themselves and are either provided by the libupipe-modules library for standard pipes, or by third-party libraries.
To instanciate managers it is necessary to link with some libraries; however there is no dependancy associated with the use of the children structures struct ubuf, struct uref and struct upump, as all code is either provided by inline functions or function pointers. Libraries providing implementations of struct upipe_mgr and struct upipe usually do not depend on libupipe.
Upon initialization, a new pipe may be passed the managers for its struct uref, struct ubuf and struct upump structures, if it requires to create them. If it has no need for a manager (for instance it just changes an attribute to all uref structures passing by), then it need not be passed.
3.3 Flows
The movement of buffers between the input and output of a pipe is called a flow. Pipes generally expect some parameters describing the flow; these are called a flow definition. A flow definition packet is a struct uref pointing to a struct udict with key/value pairs. Standard flow definitions are provided for block (uref_block_flow_alloc_def), picture (uref_pic_flow_alloc_def) and sound (uref_sound_flow_alloc_def) formats.
The input flow definition is set on a pipe using upipe_set_flow_def. It may be changed at any time, but the pipe may deny it by returning false if it considers it is too big a change; by convention, pipes only allow it if it doesn't require a full reset of the pipe's state.
Some pipes (filters) may also require a flow definition describing the requested output, for instance to change picture format. In that case the output flow definition packet is passed on allocation using upipe_flow_alloc instead of upipe_void_alloc. When a pipe changes the flow definition on its output, it must send the UPROBE_NEW_FLOW_DEF event. Upipe provides helper functions (see upipe_void_alloc_output and upipe_flow_alloc_output) to automatically forward the new flow definition to the output pipe.
It is useful to note that a flow definition packet is actually a patchwork of attributes with different purposes:
attributes which define the format of the buffers, such as the number of planes or chroma subsampling
attributes which describe several properties useful for the display or processing of the flow, such as aspect ratio, fps, bitrate, sample rate, etc.
attributes which may have no relation with the data themselves but are useful to choose between elementary streams, such as the language, program name, event information, etc.
It should therefore be considered quite standard to have frequent flow definition updates, and most pipes won't probably feel the difference.
3.4 Clocks
All dates in Upipe are represented, by convention, as ticks of a 27 MHz clock. The origin and pace of the clock depends on the variation of the date. There are three date variations stored in struct uref:
date variation | scope | description |
---|---|---|
sys | whole stream | Uses the same scale as uclock_now. The date is represented in system time. On live streams, incoming packets should be stamped with this clock, and outgoing packets (for presentation or streaming) should be scheduled according to it. A fictious system time may be necessary even for some file to file conversion, as it is the only clock that is stream-wide. |
orig | program | Carries the dates coded in the incoming stream, with the same origin, scaled to 27 MHz (if necessary). Its only use is for remultiplexing a stream with the exact same timestamps. A demux should typically retrieve this value. |
prog | program | Is based on the orig clock, but the origin may be changed to make sure that the dates are always monotonically increasing, without large gaps. A demux should typically infer this clock, and a mux should use it to write its timestamps. upipe_trickp_mgr_alloc is also able to derive the system clock from the prog clock. |
Please note that the three clocks may drift slightly. For instance, a 25 progressive frames per second stream should have a frame exactly every 40 ms according to the orig and prog clocks, but in the sys clock it may drift to 39 or 41 ms if the sender's clock is too fast or too slow. Also, when reading files, the trickplay module may create slow motion or fast forward effects by changing the pace of the sys clock.
In a struct uref, every date variation has a semantic attached to it (enum uref_date_type). There are three date types, which are used in different parts of the pipeline to represent different events:
date type | uref domain | description |
---|---|---|
Clock reference (cr) | block data | Represents the date of reception (resp. emission) of a low-level packet. It does not necessarily have a relationship with actual video or audio data. When dealing with live processing, clock references should be set on all packets entering a demux, and on all packets leaving a mux. |
Decoding timestamp (dts) | encoded frame | Represents the theorical date of decoding of an encoded audio or video frame. It must be set on all frames leaving a demux, and all frames entering a mux. |
Presentation timestamp (pts) | raw frame | Represents the theorical date of presentation of an audio or video frame. It must be set on all frames leaving a decoder, and all frames entering an encoder. |
A struct uref contains only one date type for each date variation. The date type should be cr for block data (input of demux and output of mux), dts for encoded frames (output of demux and input of mux), and pts for raw frames (output of decoder and input of encoder). This is called the "base". Dates are set using uref_clock_set_cr_prog, uref_clock_set_dts_prog, uref_clock_set_pts_prog, etc. Note that each call to uref_clock_set_XXX_prog will overwrite previous values for prog and rebase the prog date. It is possible to set the delay between the dts and pts using uref_clock_set_dts_pts_delay, and between the cr and dts using uref_clock_set_cr_dts_delay (typically the vbv delay). This makes it possible for a demux to set both dts and pts when it is available. uref_clock_rebase_dts_prog may be used by the decoder to rebase the prog date using dts. uref_clock_rebase_cr_sys would typically be used in a mux.
The struct uref also contains the sys date of the last random access point. It can be retrieved (resp. set) with uref_clock_get_rap_sys (resp. uref_clock_set_rap_sys).
3.5 Threads
Upipe objects do not natively deal with threads. Multithreading is supposed to be the prerogative of the application, or at least of very high level bin pipes (an exception being the libraries which themselves take advantage of multithreading, such as FFmpeg/libav and x264). However there is some minimum support to allow running a part of a pipeline in a different "worker" thread.
The first kind of support is the upipe_qsrc_mgr_alloc and upipe_qsink_mgr_alloc managers, which allow to bridge struct uref from one thread to another. In that case, each thread runs its own upump manager, which is passed to the pipes running in it.
Upipe also provides facilities to transfer pipes to another thread (ie. upump manager), and control them remotely from a "main" thread: upipe_xfer_mgr_alloc. It works the following way:
An xfer_mgr is created, in general from the main thread, using upipe_xfer_mgr_alloc.
A new thread is created (for instance with pthread_create). The pointer to the xfer_mgr is passed as the opaque argument to pthread_create, and retrieved in the worker thread.
The worker thread initializes a new event loop and a new upump manager, and calls upipe_xfer_mgr_attach on the xfer_mgr passed by the main thread. It can then enter the event loop.
The main thread creates a pipe that is supposed to run on the worker thread via upipe_void_alloc, and initializes it. The pipe is supposed to work with a upump_mgr, either directly, or through the use of upipe_qsrc_mgr_alloc and/or upipe_qsink_mgr_alloc. If a group of pipes has to be moved (for instance with queue source and queue sink), the group can be built normally with upipe_set_output, and then all members requiring a upump manager have to be transferred.
Pipes that need a upump manager are transferred to the worked thread by calling upipe_xfer_alloc on them. In case there are several pipes, they must be transferred in the sink to source order. After upipe_xfer_alloc, the original upipe pointer shall no longer be used in the main thread; however this function returns a pointer to a pipe handle, that allows to do basic operations on the remote pipe, such as releasing it.
At the end of the processing, all pipe handles are released, which in turn releases the remote pipes in the worker thread. The main thread calls upipe_mgr_release to stop the event loop in the worker thread, and release the xfer_mgr.