# The Media Kit: BSubscriber

Derived from: none

Declared in: be/media/Subscriber.h

Library: libmedia.so

## Overview

BSubscriber objects receive and process buffers of media-specific data. These buffers are allocated and sent to the BSubscriber by a media server; for example, buffers of audio data are sent by the Audio Server. Each server can control more than one buffer stream (the Audio Server has a sound-in stream and a sound-out stream). A BSubscriber, however, can receive buffers from only one stream at a time.

More than one BSubscriber can subscribe to the same stream at a time. The collection of BSubscribers that are subscribed to the same stream receive buffers from the stream in turn. When a BSubscriber receives a buffer it does something to it--examine or alter the data in the buffer--and then allows the server to pass it along to the next BSubscriber in the stream. This works much like a bucket-brigade: the subscribers stand side by side, and the buffer is passed from one to the next until it reaches the end of the line. Each subscriber receives a buffer containing data that may have been altered by any or all of the previous subscribers that have already received the buffer.

The media servers take care of managing the data buffers in their streams; the servers allocate new buffers, pass them from BSubscriber to BSubscriber, clear existing buffers for reuse, and so forth. A BSubscriber's primary tasks are (in this order):

- Identifying the media server it wants to get buffers from.

- Subscribing to one of the server's streams; this lets the server know that the BSubscriber wants to be allowed access to the stream.

- Entering the stream. Once a BSubscriber enters a stream, it begins receiving data buffers from the server.

- Processing the data that it finds in the buffers received from the server.

The BSubscribers that subscribe to the same stream don't have to belong to the same application. This means that your BSubscriber may be examining, adding to, or filtering data that was generated by another application.

Most buffer streams need to "flow" quickly and without interruption (this is especially true of the Audio Server's streams). The processing that a single BSubscriber performs when it receives a buffer from the server should be as brief and efficient as possible.

### Subscribing

In order to subscribe to a stream, you first need to instantiate an object that represents the stream. Currently, the Media Kit provides two classes that can be used for this purpose: BADCStream, for the ADC (audio input) stream, and BDACStream, for the DAC (audio output) stream. For example, if you plan to subscribe to the DAC stream:

       BDACStream         *outStream = new BDACStream();

`outStream` can then be used to specify the stream to which you want to subscribe. You would then subscribe to the stream like this:

       BSubscriber         *outSubscriber = new BSubscriber();
       outSubscriber->Subscribe(outStream);

This creates a new BSubscriber, called `outSubscriber`, then subscribes it to the DAC stream.

Subscription to a stream doesn't cause the subscriber to immediately begin receiving buffers from that stream; it just means that the server has given the subscriber permission to enter the stream. In order to begin receiving buffers from the stream, the `EnterStream()` function has to be called.

Think of it this way: a media server is like a public library. It has lots of wonderful audiovisual goodies you can check out, but first you have to get a library card. Once you've got your library card, you can then ask the librarian to start lending you books. Likewise, you have to subscribe to the media server before you can ask for the server to start feeding you buffers.

### Entering the Stream

Once the BSubscriber has successfully subscribed to a server's stream, it can enter the stream so it can start receiving data. You do this by calling the `EnterStream()` function:

          virtual status_t EnterStream(subscriber_id neighbor,
             bool before,
             void *userData,
             enter_stream_hook streamFunction,
             exit_stream_hook completionFunction,
             bool background)

The specifics on how `EnterStream()` is used are described in the following sections.

#### Positioning Your BSubscriber

The first two arguments to the EnterStream() function let you position the subscriber in the stream relative to another subscriber that's already in the stream.

The neighbor argument identifies the BSubscriber (by ID number, as returned by the `ID()` function) that you want the entering BSubscriber to stand next to in the stream. The before argument is used to specify whether the entering BSubscriber will be placed before (`TRUE`) or after (`FALSE`) the neighbor. The neighbor doesn't have to belong to the same application as the entering object, but it must already have entered the stream.

If you want to put the BSubscriber at the beginning or the end of the stream (or to add the first BSubscriber to the stream, in which case there are no neighbors to stand next to), specify `NULL` as the neighbor. In this case, a before value of `TRUE` indicates that the BSubscriber should be placed at the beginning of the stream, and a before value of `FALSE` indicates that the BSubscriber should be placed at the end of the stream.

If your subscriber is at the front of the stream, it will be the first to receive buffers. If it's then at the end of the stream, it will be the last to receive buffers before they're either realized (by playing through the speaker or other output device, in the case of a DAC stream) or recycled. Note, however, that a BSubscriber's position in the stream can't be locked. If you place your subscriber at the back of the stream, for example, another BSubscriber could be added to the end of the stream--possibly by another application--which would cause your BSubscriber to be bumped forward and no longer be the last subscriber in the stream.

#### Receiving and Processing Buffers

After your BSubscriber has entered the stream, it will begin receiving buffers of data. The userData, streamFunction, and background arguments to `EnterStream()` tell the server how your BSubscriber will receive those buffers.

- streamFunction is a pointer to a boolean function (the complete protocol is given below) that will be invoked once for each buffer your object receives.

- userData is a pointer-sized value that will be passed as an argument to streamFunction. You can use this to pass a reference to data your streamFunction will need.

- The value of background is used to determine whether streamFunction will be executed in a separate thread (`TRUE`) or in the same thread (`FALSE`) as that in which `EnterStream()` was called. If you run in the background, `EnterStream()` returns immediately; if not, the function doesn't return until the object has exited the stream.

Of key interest is the stream function that you must provide. This is a global C function or static C++ member function that's invoked once for each buffer received by the BSubscriber. The protocol for the function (which is `typedef`'d as `enter_stream_hook`) is:

          bool streamFunction(void *userData, char *buffer, size_t count, void *header)

- userData is the same as the userData value passed to `EnterStream()`.

- buffer is a pointer to the buffer that has just arrived.

- count is the number of bytes of data in the buffer.

- header is a pointer to the header of the sound buffer. This value can usually be ignored.

You have to implement the stream function yourself; the Media Kit doesn't supply any default stream functions for you. From within your implementation of the function, you should process the data in the buffer in whatever way your program requires. As mentioned previously, keep in mind that your stream function should be as efficient as possible.

Above all else, remember the following rule:

|  | Never clear the buffer. If you're generating new data, add it to the data already in the buffer, but don't just overwrite it. Failure to follow this important rule will make other applications (and the people using them) very unhappy. An example of this is given in the overview of the BDACStream class.

When your stream function is done processing the buffer, you simply return from the function. You don't have to do anything to cause the buffer to be sent to the next subscriber; that's handled for you by the Media Kit. The value your stream function returns is important, though. Return `TRUE` if your BSubscriber wants to keep receiving buffers or `FALSE` to be removed from the stream.

For an example of a stream function, see the overview of the BDACStream class.

#### Exiting the Stream

There are two ways to remove a BSubscriber from a stream. The first was mentioned above: return `FALSE` from the stream function. The second method is to call `ExitStream()` directly. `ExitStream()` is particularly useful if you're running the stream function in the background and you want to stop it from another thread.

Whichever method is used, the BSubscriber's completion function is invoked upon exiting the stream. This is an optional callback function, similar to the stream function, that is supplied as the completionFunction parameter to `EnterStream()`. You can specify `NULL` for this parameter if you don't want or need a completion function.

The protocol for the completion function is:

          status_t exit_stream_hook(void *userData, status_t error)

- The userData value is, again, taken from the `EnterStream()` call.

- error is a code that explains why the BSubscriber is exiting the stream.

Normally, error is `B_OK`. This means that the BSubscriber is exiting naturally--either because the stream function returned `FALSE` or because `ExitStream()` was called. If error is `B_TIMED_OUT`, then the BSubscriber is exiting because of a delay in receiving the next buffer. You can set the timeout limit through BSubscriber's `SetTimeout()` function, specifying the time limit in microseconds. By default, the object will never time out. Any other error code will have been generated by a lower-level entity and can be loosely interpreted as "something went wrong."

The completion function is executed in the same thread as the stream function. If this isn't a background thread, the value returned by the completion function is then returned by `EnterStream()`. If you are using a background thread, the value returned by the completion function is lost.

You can perform whatever clean-up is necessary in your completion function. The only thing you must never do is delete the BSubscriber itself.

## Constructor and Destructor

### BSubscriber()

          BSubscriber(const char *name = NULL)

Creates and returns a new BSubscriber object. The object can be given a name, which needn't be unique.

After creating a BSubscriber, you typically do the following (in this order):

- Subscribe the object to a buffer stream by calling `Subscribe()`.

- Allow the object to begin receiving buffers by calling `EnterStream()`.

The construction of a BSubscriber never fails.

### ~BSubscriber

          ~BSubscriber()

Destroys the BSubscriber object. You should never delete a BSubscriber from within an implementation of the object's stream function or completion function. It isn't necessary to tell the object to exit the buffer stream or to unsubscribe it before deleting; these actions will be taken automatically.

## Member Functions

### EnterStream()

          virtual status_t EnterStream(subscriber_id neighbor,
             bool before,
             void *userData,
             enter_stream_hook streamFunction,
             exit_stream_hook completionFunction,
             bool background)

Causes the BSubscriber to begin receiving data buffers from its stream. The object must have successfully subscribed to the stream by calling `Subscribe()` before it can enter the stream.

The arguments to this function are discussed in great detail in the overview to this class; read that for the scoop on how this works. Briefly, the arguments are:

- neighbor identifies the BSubscriber that this object will stand next to in the buffer stream. If neighbor is `NULL`, this BSubscriber will be positioned either at the front or the back of the buffer stream, depending on the value of the next argument.

- before, if `TRUE`, causes the BSubscriber to be placed immediately before neighbor in the stream. If it's `FALSE`, this object is placed after the neighbor in the stream. If neighbor was `NULL`, this object is placed at the front of the stream if before is `TRUE` or the back of the stream if before is `FALSE`.

- userData is a pointer-sized value that's forwarded as an argument to the stream and completion functions, which are specified in the next two arguments to `EnterStream()`.

- streamFunction is a global function that's called once for every buffer that's sent to the BSubscriber. The protocol for this function is:

          bool stream_function(void *userData, char *buffer, size_t count, void *header)

       The userData argument taken here is the same value as was passed into the EnterStream() function.  A pointer to the buffer itself is passed as buffer, and the size of the buffer, in bytes, is passed as count.  header is a pointer to the buffer's header.  If the stream function returns TRUE, the object continues to receive buffers; if it returns FALSE, the BSubscriber exits the stream.

- completionFunction is a global function that's calleda fter the BSubscriber has finished processing its last buffer. Its protocol is:

          status_t completion_function(void *userData, status_t error)

       Here, as before, userData is taken from the argument to EnterStream().  error is a code that describes why the object is exiting the stream: B_OK means that the object has received an ExitStream() call, or that the stream function returned FALSE; an error of B_TIMED_OUT means the time limit between buffer receptions, as set through SetTimeout(), has expired.

       If the function isn't running in the background (as described in the next argument), the value returned by the completion function becomes the value that's returned by EnterStream().

       The completion function is optional; a value of NULL is accepted.

- background, if `TRUE`, causes the stream and completion functions to be executed in a separate thread which is spawned for you by the Media Kit. In this case, `EnterStream()` returns immediately. If it's `FALSE`, the functions are executed synchronously within the `EnterStream()` call.

RETURN CODES

- `B_OK`. The subscriber successfully entered the stream. If background is `FALSE`, indicates that the completion function returned `B_OK`.
- `B_BAD_SUBSCRIBER`. The BSubscriber is already in the stream.
- `B_SUBSCRIBER_NOT_ENTERED`. The neighbor isn't in the buffer stream.
- `B_STREAM_NOT_FOUND`. The subscriber's stream doesn't exist.
- Other errors, as returned by the completion function (if background is `FALSE`).

See also: `ExitStream()`

### ExitStream()

          virtual status_t ExitStream(bool andWait = FALSE)

Causes the BSubscriber to withdraw from the buffer stream after it completes the processing of its current buffer. If andWait is `TRUE`, the function doesn't return until the object has completed processing this final buffer and has actually left the stream. If a completion function was supplied when `EnterStream()` was called, it will run to completion before the `ExitStream()` function returns. If andWait is `FALSE` (the default), `ExitStream()` returns immediately.

RETURN CODES

- `B_OK`. No error occurred.
- `B_BAD_SUBSCRIBER`. The object doesn't have a valid subscriber ID.
- `B_SUBSCRIBER_NOT_ENTERED`. The object isn't in the stream.
- `B_STREAM_NOT_FOUND`. The subscriber's stream doesn't exist.

### ID()

          subscriber_id ID() const

Returns the `subscriber_id` value that uniquely identifies this BSubscriber. A subscriber ID is issued when the object subscribes to a stream, and is withdrawn when the object unsubscribes. ID values are used primarily to position a BSubscriber with respect to another BSubscriber within a buffer stream.

If the BSubscriber isn't currently subscribed to any stream, `B_NO_SUBSCRIBER_ID` is returned.

This function cannot fail.

### IsInStream()

          bool IsInStream() const

Returns `TRUE` if the object is currently in a stream; otherwise it returns `FALSE`.

This function cannot fail.

### Name()

          const char *Name() const

Returns a pointer to the name of the BSubscriber. The name is set when constructing the BSubscriber and cannot be changed.

- The pointer returned by `Name()` belongs to the BSubscriber object; do not alter or dispose of it.

This function cannot fail.

### SetTimeout(), Timeout()

          void SetTimeout(bigtime_t microseconds)

          bigtime_t Timeout() const;

These functions set and return the amount of time, measured in microseconds, that a BSubscriber that has entered the buffer stream is willing to wait from the time that it finishes processing one buffer until the time that it gets the next buffer. If the time limit expires before the next buffer arrives, the BSubscriber exits the stream and the completion function is called with its error argument set to `B_TIMED_OUT`.

A time limit of 0 (the default) means no time limit--the BSubscriber will wait forever for its next buffer.

Neither `SetTimeout()` nor `Timeout()` can fail.

### Timeout() see SetTimeout()

### Subscribe()

          virtual status_t Subscribe(BAbstractBufferStream *stream)

Asks for admission into the server's list of BSubscribers to which buffers of data will be sent by the server. Subscribing doesn't cause the BSubscriber to begin receiving buffers--it simply gives the object the right to do so. To begin receiving buffers, you must invoke EnterStream() on a BSubscriber that has been successfully subscribed.

The stream is a pointer to a buffer stream derived from the BAbstractBufferStream class. For instance, to subscribe to the sound-out stream, you would instantiate a BDACStream object, and pass a pointer to that stream object to the `Subscribe()` function. Likewise, a BADCStream object would be instantiated and passed as the stream argument if you wanted to subscribe to the audio input stream.

A successful subscription returns `B_OK`.

RETURN CODES

- `B_OK`. The subscription request was successful.
- `B_BAD_PORT_ID`. The buffer stream's server is not initialized properly.
- `B_TIMED_OUT`. The buffer stream is locked and the request for access timed out.

### Unsubscribe()

          status_t Unsubscribe()

Revokes the BSubscriber's access to its media server and sets its subscriber ID to `B_NO_SUBSCRIBER_ID`. If the object is currently in a stream, it is removed from the stream automatically and the object's completion function is called.

When you delete a BSubscriber, it's automatically unsubscribed from its stream.

RETURN CODES

- `B_OK`. The BSubscriber successfully unsubscribed from the stream.
- `B_BAD_SUBSCRIBER`. The BSubscriber doesn't have a valid subscriber ID.
- `B_STREAM_NOT_FOUND`. The subscriber's stream does not exist.

The Be Book, in lovely HTML, for BeOS Release 3.

Copyright © 1998 Be, Inc. All rights reserved.

Last modified March 26, 1998.