Derived from: none
Declared in: be/media/Subscriber.h
Library: libmedia.so
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):
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.
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.
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.
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.
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.
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)
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.
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)
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.
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):
The construction of a BSubscriber never fails.
~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.
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:
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.
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.
RETURN CODES
See also: 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
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.
bool IsInStream() const
Returns TRUE if the object is currently in a stream; otherwise it returns FALSE.
This function cannot fail.
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.
This function cannot fail.
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.
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
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
The Be Book, in lovely HTML, for BeOS Release 3.
Copyright © 1998 Be, Inc. All rights reserved.
Last modified March 26, 1998.