The Media Kit: BMediaNode

The Media Kit Table of Contents | The Media Kit Index

# BMediaNode

Derived from: none

Declared in: be/media/MediaNode.h

Library: libmedia.so

Allocation: Constructor only

###### Summary

The BMediaNode class is the superclass of all participant nodes in the Media Kit. However, you'll never derive directly from BMediaNode; instead, you'll derive from one of the system interface classes which in turn are derived from BMediaNode. Look at the documentation for those other classes (such as BBufferProducer and BBufferConsumer) for details on how they're used, or see "Creating New Node Classes" on page16 for discussion on how this is done.

Because of the quirks of virtual inheritance (required by the use of multiple inheritance), your node's constructor will have to call the BMediaNode constructor. See "About Multiple Virtual Inheritance" on page21.

Applications shouldn't call a node's member functions directly; instead, you call the BMediaRoster with a reference to the node and let the request come to the node through the control port. The only exception is if the node is subclassed directly within the application, in which case `Acquire()`, `ID()`, `Node()`, and `Release()` can be called directly once the node is registered—if you do this, be sure you don't call them after the node is destroyed.

Calling a node's member functions directly from outside the node itself can result in the chain of functions involved in coordinating nodes to be called out of order. Worse, deadlock can result. So just don't do it, even if you think you've found a safe way to pull it off.

## Creating Your Own Node

### Realtime Allocators and Thread Locking

Media nodes are highly timing-sensitive creatures. The slightest delay in performing their work can cause drastic problems in media playback or recording quality. Virtual memory, normally of great benefit to users, can work against them when doing media work. A poorly-timed virtual memory hit can cause breaks in media performances.

The realtime memory allocation and locking functions provide a means for nodes to lock down their memory to prevent it from being cached to disk by the virtual memory system. This avoids situations in which the node has to pause while it or its memory is fetched back from the swap file.

The user can use the Media preference application to configure what types of nodes should use locked memory. Nodes should typically use the realtime memory allocation functions instead of `malloc()` and `free()`. `rtm_alloc()` will automatically handle locking the memory if the `B_MEDIA_REALTIME_ALLOCATOR` flag is set, so your node doesn't have to worry about it.

In addition, if the realtime flag corresponding to the type of node you're writing is set, your node should also call `media_init_realtime_thread()` to lock down the stacks of its threads. Properly-written nodes can always call `media_init_realtime_thread()`, without checking the realtime flags, because this function will return `B_MEDIA_REALTIME_DISABLED` if the corresponding flag isn't set. You can simply ignore the error and move on.

For example:

       int32 *myThreadData = rtm_alloc(4096);
       myThread = spawn_thread(myThreadFunction, "Node Thread", B_NORMAL_PRIORITY,
                &myThreadData);
       status_t err = media_realtime_init_thread(myThread, 32768,
                B_MEDIA_REALTIME_VIDEO);
       if (err != B_OK && err != B_MEDIA_REALTIME_DISABLED) {
          printf("Can't lock down the thread.n");
       }
       ...

If your node requires realtime performance from an add-on or shared library, you can use the `media_init_realtime_image()` function to lock down that image in memory. Note, however, that any uses of `malloc()` by that image won't allocate locked memory; you can't control that. Still, locking down the image itself can help performance even further.

| Standard BeOS system libraries are Be's responsibility. If it's appropriate for them to be locked, they're locked for you. Don't lock them yourself. Both libmedia.so and libroot.so have `media_init_realtime_image()` called on them.

### Negotiating a Connection

Establishing a connection between two nodes is a multi-step process. The nodes need to agree upon a data format they both support before the connection can even be established.

#### Special Considerations

If the node you're writing could be connected by the system mixer (using the Audio preference application, for example) as the default output, the node needs to be as flexible as possible in terms of the formats it accepts on its free inputs in the `GetNextInput()` function. The format your node returns from `GetNextInput()` will be used as the starting poing in the negotiation process; the more wildcards you support, the better.

An application that wants to establish a connection between some other node and your node will determine the format from the inputs into your node and the outputs from the other node, then call `BMediaRoster::Connect()` with that format.

If there are any wildcards in the format passed to `BMediaRoster::Format()`, the media roster will call `BBufferProducer::ProposeFormat()` in the node being connected to your output node; the producer will specialize the wildcards to construct the least-specific format that will guarantee that any remaining wildcards can be specialized by your node without becoming incompatible with the producer.

The resulting format may have some wildcards left (or, if the producer is particularly picky, there may be none at all). The media roster will then pass this format to your consumer node's `BBufferConsumer::AcceptFormat()` function. This function should be implemented to specialize the remaining wildcards and return this format, which should describe a specific format. This format will be used to establish the connection.

## Hook Functions

- `AddOn()

  `
- `AddTimer()

  `
- `ControlPort()

  `
- `DeleteHook()

  `
- `GetNodeAttributes()

  `
- `HandleMessage()

  `
- `NodeRegistered()

  `
- `Preroll()

  `
- `RequestCompleted()

  `
- `Seek()

  `
- `SetRunMode()

  `
- `SetTimeSource()

  `
- `Start()

  `
- `Stop()

  `
- `TimeWarp()

  `

## Constructor and Destructor

### BMediaNode()

protected:

explicit `BMediaNode(`

const char *name`)`

Call this from your derived node's constructor.

The node is created with a reference count of 1; the count is incremented each time the `Acquire()` call is issued, and decremented each time `Release()` is called. When the reference count becomes zero, the node is deleted.

### ~BMediaNode

`~BMediaNode()`

You may never delete a BMediaNode because you don't know for certain when the Media Server is done with it. Instead, the Media Server maintains a reference count for the node, and when it is no longer in use, the node will be deleted automatically.

## Member Functions

### Acquire() , Release()

BMediaNode *`Acquire(`void`) `
BMediaNode *`Release(`void`) `

`Acquire()` returns a pointer to the node, after incrementing the node's reference count.

`Release()` releases the node by decrementing its reference count. If the count reaches zero, the node is deleted and `NULL` is returned; otherwise, a pointer to the node is returned.

| Although you usually can't call node member functions directly from within an application, you can call `Acquire()` and `Release()` directly if the node is subclassed within the application itself (rather than in an add-on).

### AddNodeKind()

void `AddNodeKind(`uint64 kind`)`

Adds a kind to the set of kinds supported by the node. This lets the system know what types of node interfaces are supported by the node's implementation. Possible values include `B_BUFFER_PRODUCER` (which indicates that the node implements the BBufferProducer protocol) and `B_PHYSICAL_INPUT` (which indicates that the node implements a physical input, such as a sound digitizing input device). For a complete list of kind values, see "node_kind" on page181.

In general, you don't need to call this function. The base system classes call `AddNodeKind()` automatically to set up the node type flags; for example, a BBufferProducer automatically calls `AddNodeKind(B_BUFFER_PRODUCER)`. The only time it's necessary go call AddNodeKind() is if the node you're implementing is a physical device or a mixer, in which case you need to add the `B_PHYSICAL_INPUT`, `B_PHYSICAL_OUTPUT`, or `B_SYSTEM_MIXER` flag.

### AddOn()

virtual BMediaAddOn *`AddOn(`int32 *outInternalID`)` const = 0

Implement this function to return a pointer to the BMediaAddOn that instantiated the node. If the node lives in an application (rather than in an add-on), return `NULL`. If the node is in an add-on, outInternalID should be changed to contain the internal ID number of the node within the add-on.

### AddTimer() , TimerExpired()

virtual status_t `AddTimer(`bigtime_t toPerformanceTime, int32 cookie`) `
void `TimerExpired(`bigtime_t notifyPoint, int32 cookie, status_t error = B_OK`) `

Your node should implement the `AddTimer()` function to remember the cookie and time given. When the time toPerformanceTime is reached, your node should call `TimerExpired()` with the corresponding cookie value, passing the recorded toPerformanceTime value as the notifyPoint argument. This will, in turn, cause the `BMediaRoster::SyncToNode()` call that instigated the timer to return to the caller.

Your implementation of `AddTimer()` should return `B_OK` if all is well; otherwise it should return an appropriate error code.

### ControlPort()

virtual port_id `ControlPort(`void`)` const = 0

Returns the port_id of the port to which the node listens for requests. Your node must implement this to return a valid Kernel Kit port.

### DeleteHook()

protected:

virtual status_t `DeleteHook(`BMediaNode *node`) `

The `DeleteHook()` function is called to delete the BMediaNode object. You may augment this if you need to perform additional work before the node is deleted, but you should always either include the line:

       delete this;

or you should call through to the inherited form of the function. Return `B_OK` if the node was deleted successfully, otherwise return an appropriate error code.

### GetNodeAttributes()

virtual status_t `GetNodeAttributes(`media_node_attribute *outAttributes, size_t inMaxCount`) `

Implement this function to fill the outAttributes array (which has room for inMaxCount attributes) with your node's attributes.

Return `B_OK` if all is well, or return an appropriate error code.

### HandleBadMessage()

void `HandleBadMessage(`int32 message, const void *data, size_t size`) `

If your node receives a message that neither the node, nor any interface from which the node is derived, understands the message, pass the message along to this function, which will work magic to deal with the problem one way or another. All arguments received by the `HandleMessage()` function should be passed directly through to `HandleBadMessage()`.

### HandleMessage()

virtual status_t `HandleMessage(`int32 message, const void *data, size_t size`) `

Given a message received on the control port, this function dispatches the message to the appropriate BMediaNode hook function. If the message doesn't correspond to a hook function, `B_ERROR` is returned.

When you implement a media node of your own (derived from BBufferConsumer, BBufferProducer, etc), you always need to call through to `BMediaNode::HandleMessage()` from your node's implementation of `HandleMessage()`. This is crucial, to be sure that every ancestor of your node gets to look at the message and attempt to process it.

For example, if your node inherits from both BBufferProducer and BBufferConsumer, you should call `BBufferProducer::HandleMessage()` and `BBufferConsumer::HandleMessage()`, then `BMediaNode::HandleMessage()`, like this:

       virtual status_t MyBufferProducerConsumer::HandleMessage(int32 message,
                const void *data, size_t size) {
          if (code == SOME_THING_I_DO) {
             DoWhatever();
          }
          else if (BBufferConsumer::HandleMessage(message, data, size) &&
                   BBufferProducer::HandleMessage(message, data, size) &&
                   BMediaNode::HandleMessage(message, data, size)) {
             BMediaNode::HandleBadMessage(message, data, size);
          }
       }

Note that `BMediaNode::HandleBadMessage()` is called if none of the `HandleMessage()` implementations accept the message.

Values of code between 0x60000000 and 0x7FFFFFFF are available for use by applications. Values below 0x60000000 are reserved for use by the Media Kit, and typically correspond to specific virtual hook functions within your node. If you can show just cause for needing to know the code value for a particular hook, you can try emailing devsupport@be.com and see if we agree with you, in which case we may share that information.

Don't reverse-engineer the code values; if you really need to know, ask us. Otherwise, we won't know that a particular code shouldn't be changed. In general, it's a bad idea to rely on specific values, although there may be cases in which it's necessary.

RETURN CODES

`B_OK`. The message was dispatched.

- `B_ERROR`. The message couldn't be dispatched, possibly because it doesn't correspond to a hook function.

### HitSyncPoint() see Sync()

### ID()

media_node_id `ID``(`void`)` const

Returns the media_node_id assigned to the node by the Media Server. The result is 0 if the node hasn't been registered yet, and negative if an error occurred while attempting to register the node.

### Kinds()

uint64 `Kinds(`void`)` const

Returns a bit mask indicating what interfaces the node implements. See "node_kind" on page181 for a list of valid interface kinds.

### Name()

const char *`Name(`void`)` const

Returns a human-readable string specifying the node's name. This pointer is only valid until you `Release()` the node; after that, the pointer may point into empty space.

### NewChangeTag()

protected:

static int32 `NewChangeTag(`void`)`

This function, intended primarily for use by BBufferConsumer nodes, creates and returns a new change tag value.

### Node()

media_node `Node(`void`)` const

Returns the `media_node` structure that will be used by an application when accessing this node via the media roster.

### NodeRegistered()

virtual void `NodeRegistered(`void`)`

The Media Server calls this hook function after the node has been registered.

### NodeStopped()

status_t `NodeStopped(`bigtime_t whenPerformanceTime`)` const

When you've finished handling a stop request (buffers will no longer be flowing), call this function. If anyone is listening for stop notifications from you, they'll be notified. The whenPerformanceTime argument should be the performance time of the stop command that was handled.

Anyone listening for node stop messages will be notified; this lets applications running in offline (rendering) mode know when the node has actually completed its work.

If your node is a BBufferProducer, downstream consumers will be notified that your node stopped (automatically, no less) through the `BBufferConsumer::ProducerDataStatus(B_PRODUCER_STOPPED)` call. This lets offline rendering nodes know when each of their inputs have no more data to send for the current roll.

| This is especially important for nodes that can be run in `B_OFFLINE` mode.

RETURN CODES

`B_OK`. No error.

- Other errors. Unable to communicate with the Media Server, or an error occurred communicating with other nodes.

### Preroll()

protected:

virtual void `Preroll``(`void`) `

This hook function may be called before your node receives a `Start()` message if the application using the node calls `BMediaRoster::PrerollNode()`. This gives the node a chance to prepare the media so that when the media is started, the response is as fast as possible.

### Release() see Acquire()

### ReportError()

protected:

status_t `ReportError(`node_error whichError, const BMessage *info = NULL`) `

Transmits the error code specified by whichError to anyone that's receiving notifications from this node (see `BMediaRoster::StartWatching()` and `BMediaRoster::StopWatching()` on ). If info isn't `NULL`, it's used as a model message for the error notification message.

RETURN CODES

`B_OK`. The error report was sent without error.

- BMessage errors. The message couldn't be sent.

### RequestCompleted()

protected:

virtual status_t `RequestCompleted(`const media_request_info &info`) `

This function is called whenever a request issued by the node is completed. The info structure describes the results of the request.

The `change_tag` field in the info structure identifies the request that has been completed; this is the same value passed into the function that initiated the request.

Return `B_OK` if you're happy, otherwise return an appropriate error code.

### RunMode() , SetRunMode()

run_mode `RunMode(`void`)` const

protected:

virtual void `SetRunMode``(`run_mode mode`) `

`RunMode()` returns the node's current run_mode setting.

The `SetRunMode()` hook function is called when someone requests that your node's run mode be changed.

See also: "run_mode" on page182

### Seek()

protected:

virtual void `Seek``(`bigtime_t mediaTime, bigtime_t performanceTime`) `

This hook function is called when a node is asked to seek to the specified mediaTime by a call to the BMediaRoster.

The specified performanceTime, the time at which the node should begin the seek operation, may be in the future.

Your node is required to queue at least one each of start, stop, and seek requests, so that applications can establish, for example, both the start and stop time without having to monitor your node's progress. The actual size of these three queues is up to you. When the specified time arrives, the request should be filled.

A mediaTime value of 0 indicates the beginning of the media data.

### SetRunMode() see RunMode()

### SetTimeSource() , TimeSource()

virtual void `SetTimeSource(` BTimeSource *timeSource`) `
BTimeSource *`TimeSource(`void`)` const

The `SetTimeSource()` hook function is called when someone has requested that the node be slaved to a new time source. Augment this function to make whatever adjustments you need to make to operate at the new time scale.

`TimeSource()` returns a pointer to the BTimeSource to which the node is currently slaved. If no time source has been explicitly requested, the system time source is in use, and that's what gets returned.

The BTimeSource object returned by `TimeSource()` is only valid until the next call to `HandleMessage()` on that object. Therefore, if your node runs more than one thread, you need to serialize calls to `TimeSource()` (as well as usage of the returned objects) with calls to `HandleMessage()`. This isn't a problem if you follow the recommended policy of running a single thread that monitors the service port with `read_port_etc()` and calls `HandleMessage()` only when a message is actually received.

### Start()

protected:

virtual void `Start``(`bigtime_t performanceTime`) `

This hook function is called when a node is started by a call to the BMediaRoster.

The specified performanceTime, the time at which the node should start running, may be in the future.

Your node is required to queue at least one each of start, stop, and seek requests, so that applications can establish, for example, both the start and stop time without having to monitor your node's progress. The actual size of these three queues is up to you. When the specified time arrives, the request should be filled.

### Stop()

protected:

virtual void `Stop``(`bigtime_t performanceTime, bool immediate`) `

This hook function is called when a node is stopped by a call to the BMediaRoster.

The specified performanceTime, the time at which the node should stop, may be in the future.

If immediate is true, your node should ignore the performanceTime value and synchronously stop performance. When `Stop()` returns, you're promising not to write into any BBuffers you may have received from your downstream consumers, and you promise not to send any more buffers until `Start()` is called again.

Your node is required to queue at least one each of start, stop, and seek requests, so that applications can establish, for example, both the start and stop time without having to monitor your node's progress. The actual size of these three queues is up to you. When the specified time arrives, the request should be filled.

Nodes must recycle all buffers they may be holding onto when they're stopped.

### TimerExpired() see AddTimer()

### TimeSource() see SetTimeSource()

### TimeWarp()

protected:

virtual void `TimeWarp``(`bigtime_t atRealTime, bigtime_t newPerformanceTime`) `

This hook function is called when the time source to which the node is slaved is repositioned (via a seek operation) such that there will be a sudden jump in the performance time progression as seen by the node. The newPerformanceTime argument indicates the new performance time; the change should occur at the real time specified by the atRealTime argument.

The node should respond to this call by preparing for this change, so a serious stutter, failure, or acceleration in performance doesn't occur. Appropriate measures should be taken to minimize the impact on the performance quality; for example, a segment of the sound could be looped or skipped smoothly.

Your implementation of `TimeWarp()` should call through to `BMediaNode::TimeWarp()` as well as all other inherited forms of `TimeWarp()`.

### WaitForMessage()

protected:

status_t `WaitForMessage``(`bigtime_t waitUntil, uint32 flags = 0, void *_reserved_ = NULL`) `

This function waits until either real time specified by waitUntil or a message is received on the control port.. The flags are currently unused and should be 0.

When a message is received, the appropriate HandleMessage() calls are made given the class derivation of the node:

- `BMediaNode::HandleMessage()` is always called first.

- If the node is derived from BBufferProducer, and the message hasn't been handled yet, `BBufferProducer::HandleMessage()` is called.

- If the node is derived from BBufferConsumer, and the message hasn't been handled yet, `BBufferConsumer::HandleMessage()` is called.

- If the node is derived from BFileInterface, and the message hasn't been handled yet, `BFileInterface::HandleMessage()` is called.

- If the node is derived from BControllable, and the message hasn't been handled yet, `BControllable::HandleMessage()` is called.

- If the node is derived from BTimeSource, and the message hasn't been handled yet, `BTimeSource::HandleMessage()` is called.

- If the message still hasn't been handled, the most-derived interface's `HandleMessage()` function is called.

- If the message hasn't been handled, `HandleBadMessage()` is called.

Once this has been done, `WaitForMessage()` returns. As you can see, this can be called from your control port to handle much of the work of processing received messages.

RETURN CODES

`B_OK`. A message has occurred within the given time period.

- `B_TIMED_OUT`. The time waitUntil has arrived without a message being received.

## Constants

### node_error

Declared in: <be/media/MediaNode.h>

Constant | Description
--- | ---
`B_NODE_FAILED_START` | The node failed on a `Start()` request.
`B_NODE_FAILED_STOP` | The node failed on a `Stop()` request.
`B_NODE_FAILED_SEEK` | The node failed on a `Seek()` request.
`B_NODE_FAILED_SET_RUN_MODE` | The node's run_mode couldn't be set.
`B_NODE_FAILED_TIME_WARP` | The node couldn't fulfill a time warp request.
`B_NODE_FAILED_PREROLL` | The node failed on a `Preroll()` request.
`B_NODE_FAILED_SET_TIME_SOURCE_FO R` | The node's time source couldn't be changed.
`B_NODE_IN_DISTRESS` | The node is suffering in general.

The node_error type defines the errors a node can transmit to BMessengers that have registered to watch the node.

### node_kind

Declared in: <be/media/MediaDefs.h>

Constant | Description
--- | ---
`B_BUFFER_PRODUCER` | The flavor implements BBufferProducer.
`B_BUFFER_CONSUMER` | The flavor implements BBufferConsumer.
`B_TIME_SOURCE` | The flavor implements BTimeSource.
`B_CONTROLLABLE` | The flavor implements BControllable.
`B_FILE_INTERFACE` | The flavor implements BFileInterface.
`B_ENTITY_INTERFACE` | The flavor implements BEntityInterface (not available for R4).
`B_PHYSICAL_INPUT` | The flavor represents a physical input point (such as an input jack).
`B_PHYSICAL_OUTPUT` | The flavor represents a physical output point (such as line output).
`B_SYSTEM_MIXER` | The flavor represents the system mixer.

Defines the type or types of node that a particular flavor supports. Note that a flavor can implement more than one type of node, so you can combine these values using a bitwise or.

### run_mode

Declared in: <be/media/MediaNode.h>

Constant | Description
--- | ---
`B_OFFLINE` | Keep data accurate, even if the performance lags or runs too fast. This is typically used when rendering to disk.
`B_RECORDING` | Time-stamped buffers are being received from a node capturing them from the real world; these buffers are guaranteed to have a time stamp in the past (they're always "late").
`B_DECREASE_PRECISION` | If the performance starts to lag, try to catch up.
`B_INCREASE_LATENCY` | If the performance starts to lag, increase playout delay so buffers are delivered with less time to spare before they're needed.
`B_DROP_DATA` | If the performance starts to lag, skip data.

The run_mode type indicates how a node should cope if its performance rate deviates from the desired rate.

#### B_OFFLINE

When in offline mode (`B_OFFLINE`), the node doesn't need to worry about processing buffers at any particular time. Each buffer's performance time should be derived from the time stamped on the buffer, rather than from a BTimeSource. In fact, you'll usually want to call `set_thread_priority()` to set your node's processing threads to a low priority while the node is in offline mode. This lets software render media to disk in an efficient manner, letting the user continue to work while the render occurs in the background.

#### B_RECORDING

Recording mode (`B_RECORDING`) should be used when data is being sampled from a physical input device. These devices always deliver buffers whose time stamps are in the past (they're stamped with the time at which they were sampled, which is of course in the past, unless you've stolen a time machine from a professor from the 27th century, in which case you're probably running BeOS R127.1 and this book is woefully obsolete).

Using `B_RECORDING` mode serves to warn other nodes that the time stamps will be in the past.

#### B_DECREASE_PRECISION

In `B_DECREASE_PRECISION` mode, your node should attempt to catch up if it falls behind, by playing buffers of media data faster than normal. For audio, this might mean playing back at a higher sampling rate; for video, the frame rate might be temporarily boosted.

#### B_INCREASE_LATENCY

If your node gets behind in the `B_INCREASE_LATENCY` run mode, your node should increase its internal latency measurement and send call the LateNoticeReceived() function in anyone above your node in the media stream.

Your node should then try to produce each buffer earlier before the buffer's performance time from that point on, so there's more time for the buffers to reach their destination.

This mode is intended to compensate for data streams in which throughput can vary over time. For example, if media data is being streamed over a network, traffic fluctuations may require your node to adapt by adding more buffering (latency).

#### B_DROP_DATA

When in `B_DROP_DATA` mode, your node should simply skip buffers if if falls behind. Note that you still receive the buffers, but you should ignore any that you must in order to keep playing as many buffers as possible at the correct performance times.

The Media Kit Table of Contents | The Media Kit Index

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

Copyright © 2000 Be, Inc. All rights reserved..