![]() |
![]() |
![]() |
![]() |
![]() |
This version of this document is no longer maintained. For the latest documentation, see http://www.qnx.com/developers/docs. |
This chapter contains information about writing your own multimedia filters.
A filter is the basic building block of an application that uses the Multimedia library. There are many existing filters that handle a wide range of multimedia data formats. However, you may need to handle a new format, or want to use some hardware to process the data. In these cases, you'll need to write a new multimedia filter, or modify an existing one.
The QNX Multimedia library comes with many fully functional filters, but you may wish to write your own. This chapter shows you how to write filters, with two examples of filters for an application that handles MPEG data. Typically you won't need to create reader or writer filters, since the QNX-provided filters handle most input and output scenarios. It's more likely that you'll want to write a parser or decoder for formats that the standard filters don't handle.
The sample filters in this chapter are a parser for MPEG data, and a decoder for MPEG video data. Of course your application would also require (at least) a reader filter to read data from a stream (for example, an MPEG file), and a writer filter that writes the video data to an external device. You'd probably also want an MPEG audio decoder and audio writer.
Let's look at the generic steps you need to take to write either a codec or parser filter, and compare the differences between them.
Whether you're writing a parser or codec filter, you need to implement some or all of the methods defined in these interfaces:
| In this interface: | Implement the methods for: |
|---|---|
| AODeConstructor | Creating and destroying the filter |
| MediaInput | Managing input channels |
| MediaOutput | Managing output channels |
| MediaControl | Controlling processing |
| MediaSeeker | Seeking to a location in the media stream |
| AOResourceAccess | Exposing filter resources |
In addition, the library needs a way to query the filters on how well they can handle a media stream. The parser filter provides this functionality by implementing the AOStreamInspector interface, which rates raw data, while the codec filter implements the AOFormatInsector interface, which rates parsed data.
The filters in this example use the default MediaBufferAllocator provided by the Multimedia library to create buffers. If you want to handle your own buffer allocation and management (for example, if you have some specific hardware requirements), you need to implement this interface as well.
Let's walk through the implementation of an MPEG system parser filter and codec filter and see how all of the pieces fit together. An MPEG has at least two channels, video and audio. This parser will parse the MPEG data stream into the audio and video components, and pass them on to decoder filters that handle MPEG audio and MPEG video. The decoder filter in this example decodes the parsed MPEG video data.
An interface is declared as a static array of function pointers. You'll notice in some of the examples below that some filters that don't implement every function. In these cases, they use pointers to convenience functions in the Multimedia convenience library.
The AODeConstructor interface defines the methods that create and destroy a filter. Both the parser and decoder filters need to implement these methods:
static AODeConstructor media_filter =
{
Create,
Destroy
};
The Create() method should:
The Destroy() method should:
Let's have a closer look at how we could implement these methods for the decoder.
struct media_filter_user
{
// input channel variables
const MediaOutput *mo;
const MediaBufferAllocator *mba;
MmChannel_t *mbac;
// output channel variables
MmFormat_t format;
// codec variables and
// other variables
...
};
...
static void *Create(const AOICtrl_t *interfaces)
{
MmFilter_t *f;
// allocate our MmFilter_t filter data structure
if( !(f = (MmFilter_t*) calloc(1,sizeof(MmFilter_t))))
return 0;
// initialize and setup out identification string
if( !(f->element.ID = strdup("MPEGVIDEO_Decoder")))
return (MmFilter_t*) Destroy(f);
// flag the structure as a filter
f->element.type = MM_ELEMENT_FILTER;
// allocate our MmFilterUser_t user private data structure
if( !(f->user = (MmFilterUser_t*) calloc(1,sizeof(MmFilterUser_t))))
return (MmFilter_t*) Destroy(f);
// allocate and setup our input channel
if( !(f->ichannels=(MmChannel_t*)calloc(1,sizeof(MmChannel_t))))
return (MmFilter_t*) Destroy(f);
if( !(f->ichannels[0].element.ID = strdup("MPEGVIn"))
return (MmFilter_t*) Destroy(f);
f->ichannels[0].element.type = MM_ELEMENT_CHANNEL;
f->ichannels[0].filter = f;
f->ichannels[0].direction = MM_CHANNEL_INPUT;
f->ichannels[0].format.mf.mtype = MEDIA_TYPE_COMPRESSED|
MEDIA_TYPE_VIDEO;
// allocate and setup our output channel
if( !(f->ochannels = (MmChannel_t*) calloc(1,sizeof(MmChannel_t))))
return (MmFilter_t*) Destroy(f);
if( !(f->ochannels[0].element.ID = strdup("RawVideoOut")))
return (MmFilter_t*) Destroy(f);
f->ochannels[0].element.type = MM_ELEMENT_CHANNEL;
f->ochannels[0].filter = f;
f->ochannels[0].direction = MM_CHANNEL_OUTPUT;
f->ochannels[0].format.mf.mtype = MEDIA_TYPE_VIDEO;
/*
... mutex, condvar, etc.. initialization
*/
//success return the newly created filter
return f;
}
This function frees the resources allocated in the Create() function.
static int32_t Destroy(void *obj)
{
MmFilter_t *f = (MmFilter_t*) obj;
// make sure we have a valid pointer
if(!f)
return -1;
// free our filter private data structure
if( f->user )
free(f->user);
//free our input channel
if( f->ichannels )
{
if( f->ichannels[0].element.ID )
free(f->ichannels[0].element.ID);
free(f->ichannels);
}
// free our output channel
if( f->ochannels )
{
if( f->ochannels[0].element.ID )
free(f->ochannels[0].element.ID);
free(f->ochannels);
}
//free our filter
if( f->element.ID )
free(f->element.ID);
free(f);
// return success
return 0;
}
The Multimedia library needs to be able to query both the parser and decoder for their ability to process some data. The two types of filter implement different interfaces to accomplish this task:
Both methods return a rating from 0 to 100 (where 100 is the best) of how well the data can be handled. Filters already implemented in the library generally return 80 when they can process the data.
![]() |
We use the MmFOURCC() macro in the decoder example to check whether the format four character code matches MPEG 1 video. This macro takes the four characters that make up a fourcc code, and returns an int representation of the fourcc. |
static AOStreamInspector stream_inspector =
{
SniffData
};
static int32_t SniffData(AOIStream_t *sobj)
{
// Sniff the data and return our rating for the stream
// (0 to 100).
}
static AOFormatInspector format_inspector =
{
RateFormat,
};
static int32_t RateFormat(const AODataFormat_t *fmt)
{
/*
In the case of the mpegvideo decoder filter, we check
that we have a matching format fourcc and format type
*/
if( fmt->fourcc == MmFOURCC('M','P','1','V') &&
fmt->mtype == (MEDIA_TYPE_COMPRESSED|MEDIA_TYPE_VIDEO) )
return 95;
return 0;
}
The MediaInput interface defines methods that the Multimedia library uses to query, reserve, and release a filter's input channels. These methods contain all the functionality the library needs to connect our input channel to the output channel of the previous filter in the graph.
static MediaInput media_input =
{
IterateChannels,
AcquireChannel,
ReleaseChannel,
RateFormat,
SetFormat,
SetMediaOutput,
SetInputStream
};
Both the parser and decoder filters can use convenience functions supplied by the Multimedia library for IterateChannels(), AcquireChannel(), and ReleaseChannel(). Since the parser accepts raw data, it doesn't need to rate or set its input format. On the other hand, the decoder takes parsed data, and therefore must do both. Parsed data must have a format (a MmFormat_t) associated with it, and the decoder sets its input format so the library can tell what sort of data it can handle.
The parser's input channel is connected to streaming data, so it implements SetInputStream(). The decoder's input channel is connected to buffered data, so it implements SetMediaOutput(). This is what these methods should do:
static MediaInput media_input =
{
singleIterateInputChannels,
singleAcquireInputChannel,
singleReleaseInputChannel,
noRateInputFormat,
noSetInputFormat,
noSetMediaOutput,
SetInputStream
};
Let's take a closer look at how we would implement each of these functions:
// From the mmconvienience library:
MmChannel_t *singleIterateInputChannels(const MmFilter_t *f,int32_t
* const cookie);
// From the mmconvienience library:
int32_t singleAcquireInputChannel(MmChannel_t *c);
// From the mmconvienience library:
int32_t singleReleaseInputChannel(MmChannel_t *c);
// From the mmconvienience library:
int32_t noRateInputFormat(MmChannel_t *c,MmFormat_t *f,int32_t *
const cookie);
// From the mmconvienience library:
int32_t noSetInputFormat(MmChannel_t *c,const MmFormat_t *f);
// From the mmconvienience library:
int32_t noSetMediaOutput(MmChannel_t *c,const MediaOutput *m);
/*
This method is where a filter that connect its input
to a stream save its streamer object , and decode
enough of the input stream to figure out what the
output channels are.
*/
static int32_t SetInputStream(MmChannel_t *c,AOIStream_t *sobj)
{
/*
In the case of the mpegsystem parser filter:
- Find out how many audio and video streams
are inside the system stream. We'll just
sniff the mpeg system stream and extract the
systemheader data, using the streamer Sniff()
method.
- Allocate and setup our output channels (audio/video).
- Set up our output formats (audio/video, fourcc,
buffer size, number of buffers).
- Flag the provided channel as in use (set the
MM_CHANNEL_INPUTSET bit in the channel's flags).
- Return 0 on success, -1 on error.
*/
}
static MediaInput media_input =
{
singleIterateInputChannels,
singleAcquireInputChannel,
singleReleaseInputChannel,
RateInputFormat,
SetInputFormat,
SetMediaOutput,
noSetInputStream
};
Let's take a closer look at how we would implement each of these functions:
// From the mmconvienience library:
MmChannel_t *singleIterateInputChannels(const MmFilter_t *f,int32_t
* const cookie);
// From the mmconvienience library:
int32_t singleAcquireInputChannel(MmChannel_t *c);
// From the mmconvienience library:
int32_t singleReleaseInputChannel(MmChannel_t *c);
/*
This is where a filter that connects to the
buffered output of an other filter gives a
rating on its ability to handle the format.
Since our mpegvideo decoder filter only
handles one specific input format,
we just check that the proposed format is correct.
*/
int32_t RateInputFormat(MmChannel_t *c,MmFormat_t *f,int32_t * const
cookie);
{
if( (*cookie) != 0 )
return 0;
(*cookie)++;
if( f->mf.fourcc != MmFOURCC('M','P','1','V') || f->mf.mtype
!= (MEDIA_TYPE_VIDEO|MEDIA_TYPE_COMPRESSED))
return 0;
return 100;
}
/*
This is where a filter that connects its input
to the buffered output of an other filter would
set a negotiated input format.
In the case of the mpegvideo decoder, we just
save the negotiated format.
*/
int32_t SetInputFormat(MmChannel_t *c,const MmFormat_t *f)
{
memcpy(&c->format,fo,sizeof(MmFormat_t));
...
}
/*
This is where a filter that connects its input
to the buffered output of an other filter saves the
other filter's MediaOutput interface.
This is also a good place to initialize the decoder.
*/
int32_t SetMediaOutput(MmChannel_t *c,const MediaOutput *mo);
{
MmFilter_t *f = c->filter;
f->user->mo = mo;
c->flags |= MM_CHANNEL_INPUTSET;
/*
... initialize the decoder
*/
return 0;
}
// From the mmconvienience library:
static int32_t noSetInputStream(MmChannel_t *c,AOIStream_t *sobj)
Methods that connect your filter's output channels to another filter's input channels, either buffered or unbuffered, are defined in the MediaOutput interface.
static MediaOutput media_output =
{
IterateChannels,
AcquireChannel,
ReleaseChannel,
GetStreamer,
IterateFormats,
VerifyFormat,
SetFormat,
NextBuffer,
ReleaseBuffer,
DestroyBuffers
};
This is what these methods should do:
Let's take a closer look at how we can implement this interface:
static MediaOutput media_output =
{
IterateOutputChannels,
AcquireOutputChannel,
ReleaseOutputChannel,
noGetStreamer,
IterateOutputFormats,
acceptVerifyOutputFormats,
SetOutputFormat,
NextBuffer,
ReleaseBuffer,
DestroyBuffers
};
/* This is where we give the mmedia library access
to all of our output channels. In the case
of an mpegsystem parser, we have one audio and one
video output channel. So we iterate through and
return each channel, then NULL afterward.
*/
static MmChannel_t *IterateOutputChannels(const MmFilter_t *f,int32_t
* const cookie);
{
int32_t cnum=*cookie;
if( cnum >= f->user->nstreams )
return 0;
(*cookie)++;
return &f->ochannels[cnum];
}
/*
This is where the library flags our
output channel as being in use.
Acquire the given output channel if its
available and mark it as acquired.
Return -1 on error 0 on success.
*/
static int32_t AcquireOutputChannel(MmChannel_t *c)
{
if( c->flags&MM_CHANNEL_ACQUIRED )
return -1;
// mark as acquired
c->flags |= MM_CHANNEL_ACQUIRED;
return 0;
}
/* This is where the mmedia library flags our
output channel as being released.
Mark the given channel as no longer acquired.
*/
static int32_t ReleaseOutputChannel(MmChannel_t *c)
{
c->flags &= ~(MM_CHANNEL_ACQUIRED|MM_CHANNEL_OUTPUTSET);
return 0;
}
// From the mmconvenience library:
AOIStream_t *noGetStreamer(MmChannel_t *c);
/* This is where the library can query a
filter for all possible output formats that
a given channel has available. Since our
mpegsystem parser has 2 output channels (audio
and video) and just one output format per channel
we return our output channel format the
first time this function is called and NULL afterward.
*/
static int32_t IterateOutputFormats(MmChannel_t *c,MmFormat_t *fmt,int32_t * const cookie)
{
if( (*cookie)!=0 )
return 0;
(*cookie)++;
memcpy(fmt,&c->format,sizeof(MmFormat_t));
return 100;
}
// From the mmconvenience library:
int32_t acceptVerifyOutputFormats(MmChannel_t *c,const MmFormat_t
*f);
/* This is where the library sets the
channel negotiated output format, and the
MediaBufferAllocator interface to use to
acquire and release buffers.
Return -1 on error, 0 on success.
*/
static int32_t SetOutputFormat( MmChannel_t *c, const MmFormat_t
*fo,
const MediaBufferAllocator *mba, MmChannel_t *mbac )
{
if( !(c->flags&MM_CHANNEL_ACQUIRED) )
return -1;
if( c->flags&MM_CHANNEL_OUTPUTSET )
return -1;
if( memcmp(fo,&c->format,sizeof(MmFormat_t))
!= 0 )
return -1;
c->user->mbac = mbac;
c->user->mba = mba;
// mark the channel's output set
c->flags |= M_CHANNEL_OUTPUTSET;
return 0;
}
/* This method is called by the next filter in the
graph when it needs a buffer for the
given time. Return the filter playing status
(MM_STATUS_PLAYING, MM_STATUS_STOP, MM_STATUS_EOF,...)
*/
static int32_t NextBuffer(MmChannel_t *c,MmTime_t t,MmBuffer_t **buffer)
{
/*
In the case of the mpegsystem parser filter:
- if we are not MM_STATUS_PLAYING return our status;
- check channel stream id (audio/video )
- acquire the next buffer with the channel's MediaBufferAllocator
interface (mba->AcquireBuffer())
- fill the buffer with data
- return current playing status
- the Library handles releasing the buffer
*/
}
/*
When the next filter in the graph
is finished with the buffer we gave it in
NextBuffer(), it calls this function to release
it back into its pool. In the case of the mpegsystem
parser filter, call the ReleaseBuffer()
function provided through the MediaBufferAllocator
interface.
*/
static int32_t ReleaseBuffer(MmChannel_t *c,MmBuffer_t *b)
{
return c->user->mba->ReleaseBuffer(c->user->mbac,b);
}
/*
This function is called by the library when
an output channel is being released.
In the case of the mpegsystem parser filter,
call the FreeBuffer() function
provided through the MediaBufferAllocator interface.
*/
static int32_t DestroyBuffers(MmChannel_t *c)
{
if( c->user->mba )
c->user->mba->FreeBuffers(c->user->mbac);
return 0;
}
Let's take a closer look at how we would implement this interface:
static MediaOutput media_output =
{
singleIterateOutputChannels,
AcquireOutputChannel,
ReleaseOutputChannel,
noGetStreamer,
IterateOutputFormats,
acceptVerifyOutputFormats,
SetOutputFormat,
NextBuffer,
ReleaseBuffer,
DestroyBuffers
};
// From the mmconvenience library:
static MmChannel_t *singleIterateOutputChannels(const MmFilter_t
*f,int32_t * const cookie);
/*
This is where the library flags
our output channel as being in use.
Acquire the given output channel if its
available and mark it as acquired.
Return -1 on error 0 on success.
*/
static int32_t AcquireOutputChannel(MmChannel_t *c)
{
MmFilter_t *f=c->filter;
// Make sure the output channel isn't already acquired
if( c->flags&MM_CHANNEL_ACQUIRED )
return -1;
/* Make sure our input channel has already
been acquired and its input set
since otherwise we won't know the dimensions,
etc of our output channel
*/
if( !(f->ichannels[0].flags&MM_CHANNEL_INPUTSET)
)
return -1;
// Flag the output channel as acquired
c->flags |= MM_CHANNEL_ACQUIRED;
return 0;
}
/*
This is where the library flags our output
channel as being released. Mark the given channel
as no longer acquired.
*/
static int32_t ReleaseOutputChannel(MmChannel_t *c)
{
c->flags &= ~(MM_CHANNEL_ACQUIRED|MM_CHANNEL_OUTPUTSET);
return 0;
}
/*
This is where a filter can give another filter a
streamed interface to one of its output channel(s).
Since our mpegvideo decoder uses buffered output channels
we just return NULL;
This function has been implemented for you in the
mmconvenience library and has the following prototype:
*/
AOIStream_t *noGetStreamer(MmChannel_t *c);
/*
This is where the mmedia library can query a filter
for all possible output formats that a given channel
has available. Our mpegvideo decoder has quite a
few output formats for the video output channel.
So we just go through all of them, returning one
format at a time, and incrementing the iterator.
Return 0 when we are done.
*/
static int32_t IterateOutputFormats(MmChannel_t *c,MmFormat_t *fmt,int32_t
* const cookie)
{
// save our iterator
int32_t cnum = *cookie;
// if our iterator >= 5 we are done
if( cnum >= 5 )
return 0;
// Initialize our proposed output format with a known value
memset(fmt,0,sizeof(MmFormat_t));
memcpy(&fmt->mf,&c->format.mf,sizeof(AODataFormat_t));
fmt->mf.mtype = MEDIA_TYPE_VIDEO;
fmt->min_buffers = 1;
// fill out some specific output format value according to this
iteration
switch( cnum )
{
case 0:
fmt->mf.fourcc = MmFOURCC('Y','U','Y','2'); // Pg_VIDEO_FORMAT_YUY2
fmt->mf.u.video.depth =16;
break;
case 1:
fmt->mf.fourcc = MmFOURCC('R','G','B','2'); // Pg_IMAGE_DIRECT_8888:
fmt->mf.u.video.depth = 32;
break;
case 2:
fmt->mf.fourcc = MmFOURCC('R','G','B','4'); // Pg_IMAGE_DIRECT_888:
fmt->mf.u.video.depth = 24;
break;
case 3:
fmt->mf.fourcc = MmFOURCC('R','G','B','6'); // Pg_IMAGE_DIRECT_565:
fmt->mf.u.video.depth = 16;
break;
case 4:
fmt->mf.fourcc = MmFOURCC('R','G','B','5'); // Pg_IMAGE_DIRECT_555:
fmt->mf.u.video.depth = 16;
break;
default:
break;
}
// Adjust our format min buffer size according to this iteration
fmt->min_buffersize = fmt->mf.u.video.width
* fmt->mf.u.video.height * ((fmt->mf.u.video.depth+7)>>3);
// Increment our iterator for next iteration
(*cookie)++;
return 100;
}
/*
This is where the mmedia library gives the filter
a last chance to accept or reject a negotiated
output format for a given channel. Return 0 to
reject the proposed output format, 100 to accept
it. This method has been implemented for you in
the mmconvenience library.
This method has the following prototype:
*/
int32_t acceptVerifyOutputFormats(MmChannel_t *c,const MmFormat_t
*f);
/*
This is where the library sets the channel
negotiated output format, and the MediaBufferAllocator
interface is used to acquire/release buffers.
Return -1 on error, 0 on success.
*/
static int32_t SetOutputFormat( MmChannel_t *c, const MmFormat_t
*fo,
const MediaBufferAllocator *mba, MmChannel_t *mbac )
{
if( c->flags&MM_CHANNEL_OUTPUTSET )
return -1;
/* Save the MediaBufferAllocator interface our
output channels will use
to allocate an output buffer and its
associated channel.
*/
c->user->mbac = mbac;
c->user->mba = mba;
// save the negotiated output format
memcpy(&c->format,fo,sizeof(MmFormat_t));
// mark the channel's output set
c->flags |= M_CHANNEL_OUTPUTSET;
return 0;
}
/*
This function is called by the next filter
in the graph when it needs a buffer for the
given time. Return the filter playing status
(MM_STATUS_PLAYING, MM_STATUS_STOP,MM_STATUS_EOF,...)
*/
static int32_t NextBuffer(MmChannel_t *c,MmTime_t t,MmBuffer_t **buffer)
{
/*
In the case of the mpegvideo decoder filter:
- If we are not MM_STATUS_PLAYING return our status
- Acquire a buffer through the channel's MediaBufferAllocator
interface (mba->AcquireBuffer())
- If we're at the EOF, release the buffer; otherwise:
- Fill the buffer with data
- Return current playing status
*/
}
/*
When the next filter in the graph
is finished with the buffer we gave it in NextBuffer(),
it calls this function to release it back into its pool.
In the case of the mpegvideo decoder filter just call
the ReleaseBuffer() function provided through the
MediaBufferAllocator interface.
*/
static int32_t ReleaseBuffer(MmChannel_t *c,MmBuffer_t *b)
{
return c->user->mba->ReleaseBuffer(c->user->mbac,b);
}
/*
This function is called by the library when
an output channel is being released.
In the case of the mpegvideo decoder filter we call
the FreeBuffer() function provided through the
MediaBufferAllocator interface.
*/
static int32_t DestroyBuffers(MmChannel_t *c)
{
if( c->user->mba )
c->user->mba->FreeBuffers(c->user->mbac);
return 0;
}
The MediaControl interface defines the methods for starting, stopping, pausing, and resuming media filters.
static MediaControl media_control =
{
Start,
Stop,
Pause,
Resume,
Status
};
These methods should do the following:
Both our parser and decoder would implement these methods:
static int32_t Start(MmFilter_t *f,MmTime_t media_time)
{
// start the filter
}
/ static int32_t Stop(MmFilter_t *f)
{
// stop the filter
}
static int32_t Pause(MmFilter_t *f)
{
// pause the filter
}
static int32_t Resume(MmFilter_t *f,MmTime_t media_time,MmTime_t
real_time)
{
// resume the filter
}
static int32_t Status(MmFilter_t *f)
{
// return the playing status
}
The MediaSeeker interface defines the seek method, and the filters that need to be informed when the graph is seeking to a new location.
static MediaSeeker media_seeker =
{
Seek
};
The Multimedia library calls this function when the graph needs to seek to a certain location in the media. Theoretically, all the filter has to do is empty its buffers, and let the normal AOStreamer handle the rest.
static int32_t Seek(MmFilter_t *f, MmTime_t media_time)
{
// to do: seek to a new position
}
The AOResourceAccess interface defines the methods that expose any internal resources to the outside world for reading or writing.
static AOResourceAccess resource_access =
{
GetResources,
SetResource,
};
These methods should do the following:
static const AOResource_t *GetResources(void *handle);
static int32_t SetResource(void *handle,const char *res,const void *data);
You need to implement this interface only for filters that need to expose their internal resources.
In this example we implement both these functions.
The Addon Interface library also defines the type AOResource_t that's used for internal resource storage and handling data.
typedef struct
{
char *name; // name of resource
char *description; // description of resource
void *value; // filled in later with the value
void *info; // typing info (ie range, list of items, etc)
int32_t type; // AOR_TYPE_* flags
} AOResource_t;
Let's assume that our filter wants to expose the following resources:
MmTime_t position; // read-only resource
MmTime_t duration; // read-only resource
uint32_t debug; // read/write resource
In our filter's internal data structure we have:
struct media_filter_user
{
/*
other data
*/
uint32_t debug; // put the filter in debug mode
MmTime_t position; // position in the stream
MmTime_t duration; // duration of the stream
AOResource_t *res; // data structure needed to store or handle
resources
};
We need to define a AOResource_t record for each one of these resources:
static const MmTime_t timerange[] = {0,86400000000,1}; // min,
max, default value
static const int32_t debugrange[] = {0,10,0}; // min, max, default
value
static const AOResource_t resources[] =
{
{"Duration","Duration",(void*)offsetof(struct
media_filter_user,duration),&timerange,
AOR_TYPE_LONGLONG|AOR_TYPE_READABLE },
{"Position","Position",(void*)offsetof(struct
media_filter_user,position),&timerange,
AOR_TYPE_LONGLONG|AOR_TYPE_READABLE },
{"Debug","Debug Output",(void*)offsetof(struct media_filter_user,debug),&
debugrange,
AOR_TYPE_POINTER|AOR_TYPE_READABLE|AOR_TYPE_WRITABLE
},
{ 0 }
};
In the Create() method of the AODeConstructor interface, we need to allocate some memory and set up our resource pointers:
static void *Create(const AOICtrl_t *interfaces)
{
MmFilter_t *f;
AOResource_t *res;
// create the filter object
if( !(f = (MmFilter_t*) calloc(1,sizeof(MmFilter_t))) )
return 0;
// allocate the filter user data
if( !(f->user = (MmFilterUser_t*) calloc(1,sizeof(MmFilterUser_t)))
)
return (MmFilter_t*) Destroy(f);
// allocate our resource data structure
if( !(f->user->res = (AOResource_t*) malloc(sizeof(resources))))
return (MmFilter_t*) Destroy(f);
// initialize the resource data structure
memcpy(f->user->res,&resources,sizeof(resources));
res = f->user->res;
// adjust the resources pointers to the correct offset value
while( res->name )
{
char *p = (char*) f->user;
res->value = (void*) (&p[(int32_t)res->value]);
res++;
}
/*
....
*/
return f;
}
And finally, the implementation of the GetResources() and SetResource() functions of the AOResourceAccess interface:
static const AOResource_t *GetResources(void *handle)
{
MmElement_t *e = (MmElement_t*) handle;
if( e && e->type==MM_ELEMENT_FILTER
)
{
MmFilter_t *f = (MmFilter_t*) handle;
// success
// return a pointer to our resource data structure
return f->user->res;
}
return 0;
}
static int32_t SetResource(void *handle,const char *name,const void
*data)
{
MmElement_t *e = (MmElement_t*) handle;
if( e && e->type == MM_ELEMENT_FILTER
)
{
MmFilter_t *f = (MmFilter_t*) handle;
AOResource_t *res = f->user->res;
while( res->name )
{
if( strcmp(res->name,name) == 0 )
{
// found it!
if(strcmp(name,"Debug") == 0 )
{
//fprintf(stderr, "setting debug to %d\n", (int32_t)data);
f->user->debug = (int32_t) data;
}
return 0;
}
res++;
}
}
return -1;
}
The Multimedia library uses the Addon Interface (AOI) library to load multimedia filters and perform multimedia format negotiations at runtime. This means that if you export the set of interfaces discussed above and drop your filter compiled as a DLL into the /lib/mmedia/dll directory, any multimedia application that uses the multimedia architecture will be able to use the services your filter provides without recompilation.
Our exported mpegsystem parser interface list:
#ifdef VARIANT_dll
AOInterface_t interfaces[] =
#else
AOInterface_t mpegs_parser_interfaces[] =
#endif
{
{ "Name",1,"mpegs_parser" },
{ "Version",MM_VERSION,0 },
{ "AODeConstructor",AODECONSTRUCTOR_VERSION,&media_filter
},
{ "MediaInput",MM_INPUT_VERSION,&media_input },
{ "MediaOutput",MM_OUTPUT_VERSION,&media_output },
{ "MediaSeeker",MM_SEEKER_VERSION,&media_seeker },
{ "MediaControl",MM_CONTROL_VERSION,&media_control },
{ "AOStreamInspector",AOSTREAMINSPECTOR_VERSION,&stream_inspector
},
{ "AOResourceAccess",AORESOURCEACCESS_VERSION,&resource_access
},
{ 0,0 }
};
Our exported mpegvideo decoder interface list:
#ifdef VARIANT_dll
AOInterface_t interfaces[] =
#else
AOInterface_t mpegv_decoder_interfaces[] =
#endif
{
{ "Name",1,"mpegv_decoder" },
{ "Version",MM_VERSION,0 },
{ "AODeConstructor",AODECONSTRUCTOR_VERSION,&media_filter
},
{ "MediaInput",MM_INPUT_VERSION,&media_input },
{ "MediaOutput",MM_OUTPUT_VERSION,&media_output },
{ "MediaSeeker",MM_SEEKER_VERSION,&media_seeker },
{ "MediaControl",MM_CONTROL_VERSION,&media_control },
{ "AOFormatInspector",AOFORMATINSPECTOR_VERSION,&format_inspector
},
{ 0,0 }
};
![]() |
![]() |
![]() |
![]() |