Table of Contents

Using Reggae in Applications


Adding Reggae to an application is easy. The first step is to add needed includes:

#include <proto/multimedia.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <clib/alib_protos.h>

The first file contains definition of multimedia.class library API. It also includes <classes/multimedia/multimedia.h>, containing Reggae structures, constants, tags and macros. The rest of includes are not Reggae specific, in fact most projects include them anyway, as they define basic system services. There are also additional Reggae headers, their including depends on application and is covered in other articles.

Now we are ready for Reggae initialization. All what has to be done is opening multimedia.class just like an ordinary MorphOS shared library. The only noticeable difference is that library name contains path part, as Reggae classes are not directly on library search path:

struct Library* MultimediaBase;

if (MultimediaBase = OpenLibrary("multimedia/multimedia.class", 55))
  /* Now Reggae is ready to use until the library is closed. */


As with every library, the name of the base is important, as it is implicitly used in all calls to the library API as a hidden parameter. Note also the usual error handling, multimedia.class is disk-based and also performs some disk activity at startup, then checking the library base against NULL is recommended. 55 is the current multimedia.class version. Applications should request this version, as previous ones have some important features missing. Reggae cleanup is done with classic CloseLibrary() call.

Note: Some programmers use automatic library opening and closing by linking with libauto. multimedia.class can be added to the list of automatically opened libraries, but it is not added by default.

A complete example code shows Reggae initialization. The example opens multimedia.class and, if it succeeded, prints the version and revision of multimedia.class to the console.

Opening and Closing Reggae Classes

Reggae system consists of disk based BOOPSI classes. Every Reggae class is a shared library, which creates BOOPSI class in memory when it is opened for the first time. This modular design allows for dynamic expunge of classes which are no more used. Because every Reggae class is a shared library, it is loaded with usual OpenLibrary() call.

An external (disk based) BOOPSI class can have two API-s. The first one is BOOPSI API reached with DoMethod(). The second one is an ordinary shared library API. Reggae classes have only the first, object oriented one. The only exception is multimedia.class, being the base class of Reggae (all other classes are subclasses of it). The multimedia.class library API contains functions not operating on objects, or operating on sets of objects. Then the first step for every program using Reggae is to open the multimedia.class.

NewObject() and MediaNewObject()

These two functions have similar names, but they are different. Programmers using MUI may think that MediaNewObject() is some kind of wrapper around NewObject(), similarly to MUI_NewObject() being such a wrapper. This is not true for MediaNewObject() however. NewObject() is a basic BOOPSI call, and just creates an object of specified class. Reggae objects are created with this function as well.

MediaNewObject() creates a complete tree of objects needed to decode a media stream to basic formats. The tree consists of stream object, one or more demultiplexer objects, one or more decoder objects and a few auxiliary ones like multiread buffers or processblocks. The whole tree is presented to the application as a single object delivering decoded streams on its output ports.

Another difference between the two functions is automatic loading of Reggae classes. NewObject() call does not load a disk based BOOPSI class into memory automatically. Then an application must load such a class manually with OpenLibrary() before object creation (and unload it after use with CloseLibrary()). On the other hand MediaNewObject() opens all needed classes automatically and takes care of their unloading.

Error Checking

Many operations on Reggae objects may fail for different reasons. It may be just memory shortage, I/O error, wrong arguments passed by application etc. A good programming practice is to verify results of operations and take appropriate actions in case of failure. The first step of verification is checking method return value. Its exact meaning depends on the method itself, and is described in autodocs. The common cases are described below. The second stage of error checking is to get an error code of operation. This is done by getting a MMA_ErrorCode attribute value from an object. Error code values are defined in <classes/multimedia/multimedia.h> header file. The usual way is just getting the attribute from an object, which is done with BOOPSI call GetAttr().

LONG error;

GetAttr(object, MMA_ErrorCode, (ULONG)&error);

Alternatively, one can get the attribute through any of object's port (input or output, it does not matter):

error = MediaGetPort(object, 0, MMA_ErrorCode);

Errors During Object Creation

Creation of an object is a special case in error handling for two reasons. Firstly, this is the most probable place for errors, as creating a new Reggae object may be a complex operation involving creation of many basic objects, connecting them, performing I/O operations on data source, parsing stream headers etc. Secondly – if object creation fails, there is no object to ask for MMA_ErrorCode attribute. That is why MMA_ErrorCode can be passed to OM_NEW() method (that means NewObject() and MediaNewObject() accept it), and its tag value contains a pointer to a variable, where error code will be placed. An example code below shows error handling during object creation:

Object *obj;
LONG error = 0; /* initialization is recommended */

if (obj = MediaNewObjectTags( /* any tags here */ , MMA_ErrorCode, (IPTR)&error, TAG_END))
  /* use object */
  /* error code is in 'error' variable, take some action, inform user etc. */

Errors During Data Pulling

The first stage of error checking in this case is checking the return value of MMM_Pull() method. When there is no error, it should be equal to the amount of bytes requested. If it is not the case, one should get MMA_ErrorCode value, as described above. There is one case however, when the value returned by MMM_Pull() will be not equal to the request size, and MMA_ErrorCode will be zero. It can happen, when the request size is not aligned to the smallest chunk allowed by Reggae. This chunk is a single pixel for video, and a single sample frame for audio. Such a misaligned pull request is truncated silently and only the whole chunks are returned. That is why aligning request size to the size of a pixel or sample frame should be a rule of thumb while programming Reggae. A code fragment below demonstrates error checking after MMM_Pull():

if ((pulled = DoMethod(MMM_Pull, object, port, buffer, length)) == length)
  /* Data pull has been succesfull. */
  LONG error = 0;

  GetAttr(object, MMA_ErrorCode, (IPTR)&error);

  /* Data pull failed, some data can be valid however (number of valid
     data bytes is stored in 'pulled'). Error code is in 'error'. */

Displaying Error Messages

Some of Reggae errors (especially those, which are somehow expected, like file or network I/O errors) should be reported to user. Note that for less probable errors advanced users may use MediaLogger, which shows Reggae event log. Basic problems however should be reported by application. Of course displaying some cryptic error code is not recommended. The simplest way is to use MediaFault() function to convert these codes into text messages. A big advantage of MediaFault() is that its messages are localized, so it automatically returns texts in user's preferred language. The function may be for example used as follows:

LONG error;

GetAttr(object, MMA_ErrorCode, (IPTR)&error);

if (error) Printf("Operation failed: %s.\n", MediaFault(error));
This simple example prints message to a console, GUI based programs will pass it to MUI_Request() for example. Strings returned by MediaFault() are not capitalized and have no dot at end.