Chapter 23. Pre-made base classes

Table of Contents

Writing a sink
Writing an audio sink
Writing a video sink
Writing a source
Writing an audio source
Writing a transformation element

So far, we've been looking at low-level concepts of creating any type of GStreamer element. Now, let's assume that all you want is to create an simple audiosink that works exactly the same as, say, esdsink, or a filter that simply normalizes audio volume. Such elements are very general in concept and since they do nothing special, they should be easier to code than to provide your own scheduler activation functions and doing complex caps negotiation. For this purpose, GStreamer provides base classes that simplify some types of elements. Those base classes will be discussed in this chapter.

Writing a sink

Sinks are special elements in GStreamer. This is because sink elements have to take care of preroll, which is the process that takes care that elements going into the GST_STATE_PAUSED state will have buffers ready after the state change. The result of this is that such elements can start processing data immediately after going into the GST_STATE_PLAYING state, without requiring to take some time to initialize outputs or set up decoders; all that is done already before the state-change to GST_STATE_PAUSED successfully completes.

Preroll, however, is a complex process that would require the same code in many elements. Therefore, sink elements can derive from the GstBaseSink base-class, which does preroll and a few other utility functions automatically. The derived class only needs to implement a bunch of virtual functions and will work automatically.

The base class implement much of the synchronization logic that a sink has to perform.

The GstBaseSink base-class specifies some limitations on elements, though:

  • It requires that the sink only has one sinkpad. Sink elements that need more than one sinkpad, must make a manager element with multiple GstBaseSink elements inside.

Sink elements can derive from GstBaseSink using the usual GObject convenience macro G_DEFINE_TYPE ():

G_DEFINE_TYPE (GstMySink, gst_my_sink, GST_TYPE_BASE_SINK);

[..]

static void
gst_my_sink_class_init (GstMySinkClass * klass)
{
  klass->set_caps = [..];
  klass->render = [..];
[..]
}
    

The advantages of deriving from GstBaseSink are numerous:

  • Derived implementations barely need to be aware of preroll, and do not need to know anything about the technical implementation requirements of preroll. The base-class does all the hard work.

    Less code to write in the derived class, shared code (and thus shared bugfixes).

There are also specialized base classes for audio and video, let's look at those a bit.

Writing an audio sink

Essentially, audio sink implementations are just a special case of a general sink. An audio sink has the added complexity that it needs to schedule playback of samples. It must match the clock selected in the pipeline against the clock of the audio device and calculate and compensate for drift and jitter.

There are two audio base classes that you can choose to derive from, depending on your needs: GstAudioBasesink and GstAudioSink. The audiobasesink provides full control over how synchronization and scheduling is handled, by using a ringbuffer that the derived class controls and provides. The audiosink base-class is a derived class of the audiobasesink, implementing a standard ringbuffer implementing default synchronization and providing a standard audio-sample clock. Derived classes of this base class merely need to provide a _open (), _close () and a _write () function implementation, and some optional functions. This should suffice for many sound-server output elements and even most interfaces. More demanding audio systems, such as Jack, would want to implement the GstAudioBaseSink base-class.

The GstBaseAusioSink has little to no limitations and should fit virtually every implementation, but is hard to implement. The GstAudioSink, on the other hand, only fits those systems with a simple open () / close () / write () API (which practically means pretty much all of them), but has the advantage that it is a lot easier to implement. The benefits of this second base class are large:

  • Automatic synchronization, without any code in the derived class.

  • Also automatically provides a clock, so that other sinks (e.g. in case of audio/video playback) are synchronized.

  • Features can be added to all audiosinks by making a change in the base class, which makes maintenance easy.

  • Derived classes require only three small functions, plus some GObject boilerplate code.

In addition to implementing the audio base-class virtual functions, derived classes can (should) also implement the GstBaseSink set_caps () and get_caps () virtual functions for negotiation.

Writing a video sink

Writing a videosink can be done using the GstVideoSink base-class, which derives from GstBaseSink internally. Currently, it does nothing yet but add another compile dependency, so derived classes will need to implement all base-sink virtual functions. When they do this correctly, this will have some positive effects on the end user experience with the videosink:

  • Because of preroll (and the preroll () virtual function), it is possible to display a video frame already when going into the GST_STATE_PAUSED state.

  • By adding new features to GstVideoSink, it will be possible to add extensions to videosinks that affect all of them, but only need to be coded once, which is a huge maintenance benefit.