BMediaNode¶
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
” 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
”.
Note
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_realtime_init_thread()
to lock down the stacks of its
threads. Properly-written nodes can always call
media_realtime_init_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_realtime_init_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.
Note
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_realtime_init_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.