Frame filtering

Source and sink elements in a Savant pipeline can include a filter function. This function is called for every frame, and can be used to filter out frames that should not be processed by the pipeline (ingress filter in a source element) or frames that should not be passed to the next pipeline (egress filter in a sink element).

This is useful, for example, in the case Conditional Encoding is defined and as a result the pipeline produces frames without video data. Egress filtering can be used to filter out these frames before they are passed to the next pipeline.

Frame filtering functions work similarly to Python Function Unit, with a couple of differences.

Filtering function config

To define an ingress filtering function, add an ingress_frame_filter node to the SourceElement definition, e.g.

source:
  element: zeromq_source_bin
  properties:
    socket: router+bind:ipc:///tmp/zmq-sockets/input-video.ipc
  ingress_frame_filter:
    module: path.to.module
    class_name: IngressFilter

Ingress filter can also be configured for non-zeromq sources, such as uridecodebin:

source:
  element: uridecodebin
  properties:
    uri: file:///data/file.mp4
    source_id: my-source
  ingress_frame_filter:
    module: path.to.module
    class_name: IngressFilter

Note

When using a non-zeromq source (e.g. uridecodebin), the source_id property can be set in properties to assign a meaningful source identifier. Without it, the source ID defaults to the GStreamer pad index (e.g. 0).

Likewise for egress filtering, add an egress_frame_filter node to the SinkElement definition.

sink:
  - element: zeromq_sink
    properties:
      socket: pub+bind:ipc:///tmp/zmq-sockets/output-video.ipc
    egress_frame_filter:
      module: path.to.module
      class_name: EgressFilter

Defining the filtering functions is optional. By default, ingress filters out all frames without video, and egress is bypassed.

Filtering function code

Custom ingress and egress filters must implement BaseFrameFilter interface. The interface defines one method:

def __call__(self, video_frame: VideoFrame) -> bool:
    """Return True if the frame should be processed, False otherwise."""

Default ingress filter provides an example of filter that drops frames without video data:

class DefaultIngressFilter(BaseFrameFilter):
    """Default ingress filter, filters out frames with no video data."""

    def __call__(self, video_frame: VideoFrame) -> bool:
        """Filters input frames.

        :param video_frame: Video frame.
        :return: Whether to accept the frame into the pipeline (True)
            or skip it (False).
        """

        return not video_frame.content.is_none()