The Be Book The Media Kit The Media Kit Index

BMediaNode

Derived from: none
Declared in:  be/media/MediaNode.h
Library: libmedia.so
Allocation: Constructor only
Summary:  more...


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


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 page182.

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 (message == 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 message 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 message 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 message values; if you really need to know, ask us. Otherwise, we won't know that a particular message code number 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


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 page182 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


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


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.


Set 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:

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


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_FOR 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 Be Book The Media Kit The Media Kit Index

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

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