This appendix contains information about using resources in your addon. A resource is any piece of data that you want to have access to. For example, in a multimedia interface, you may want to specify “volume” as a resource. You can then write functions to get and set the volume, using GetResource() and SetResource() as defined in the AOResourceAccess interface.
Here's an example of working with a resource in an addon. Lets start with creating resources in your addon. One approach is to create a const structure containing your resources definitions, and then use that as a template to create a resources structure for each context, if your addon uses contexts. You would use contexts if you needed more than one instance of the same addon. Lets create a context structure first:
typedef struct my_context { int32_t volume; // volume, 0-100 int64_t position; // position, in microseconds AOResource_t *resources; };
We'll need typing info for our volume and position resources, such as minimum, maximum, and increment values. In an AOResource_t there is pre-defined typing info in the type element flags. When the type flag for an AOResource_t is AOR_TYPE_LONG, then value is an int32_t and the resource info is a pointer to int32_t min, max, and step values:
// volume range from 0 to 100, in increments of one static const int32_t volumerange[] = {0,100,1};
When type is AOR_TYPE_LONGLONG, the resource value is an int64_t and info is a pointer to int64_t min, max, and step values:
// position range from 0 to 86400000000 (86400 seconds, or 24 hours.) static const int64_t posrange[] = {0,86400000000,1};
The AOResource_t structure is defined as:
typedef struct { char *name; char *description; void *value; void *info; int32_t type; } AOResource_t;
For more information about the AOResource_t structure, see AOResource_t.
Now we can define our const AOResource_t resources structure:
static const AOResource_t resources[] = { { "Volume","Current Volume",(void*)offsetof(my_context,volume), &volumerange,AOR_TYPE_LONG|AOR_TYPE_READABLE|AOR_TYPE_WRITABLE }, { "Position","Current Position",(void*)offsetof(my_context,position), &posrange,AOR_TYPE_LONGLONG|AOR_TYPE_READABLE|AOR_TYPE_WRITABLE }, { 0 } };
As you can see, the pointer to the current value of the resource is not valid; its an offset into the context. When we create a new context, we'll have to adjust this pointer accordingly. Assuming we use the AODeConstructor interface, our create function might look like:
static void *Create(const AOICtrl_t *interfaces) { my_context *ctx=(my_context*)calloc(1,sizeof(my_context); int32_t n; AOResource_t *res; // allocate new resource structure, and copy const version // into it: ctx->res=(AOResource_t*)malloc(sizeof(resources)); memcpy(ctx->res,&resources,sizeof(resources)); for (res=ctx->res;res->name;res++) { char *p=(char *)ctx; // Add the address of the context to the offset, making // the value pointer now point to the correct location // in our context: res->value=(void*)(&p[(int32_t)res->value]); } // initialize our context elements, if necessary ctx->volume=50; return ctx; } static void Destroy(void *p) { my_context *ctx=(my_context*)p; free(ctx->res); free(ctx); } static AODeConstructor media_filter = { Create, Destroy };
If we want the outside world to be able to access our resources, we'll need to implement the AOResourceAccess interface. This is quite easy as well:
static const AOResource_t *GetResources(void *handle) { my_context *ctx=(my_context*)handle; return handle->resources; } static int32_t SetResource(void *handle,const char *res, const void *data) { my_context *ctx=(my_context*)handle; // first resource is volume if (strcmp(res,ctx->resources[0].name)==0) { ctx->volume=*((int32_t*)data); // do any other volume control stuff here // return success return 0; } else // second resource is position if (strcmp(res,ctx->resources[1].name)==0) { ctx->position=*((int64_t*)data); // do any other positioning stuff here // return success return 0; } // no matching resource, return error return -1; } static AOResourceAccess resource_access = { GetResources, SetResource, };
At the end of our addon, we put them all together in our interfaces list:
#ifdef VARIANT_dll AOInterface_t interfaces[] = #else AOInterface_t my_addon_interfaces[] = #endif { { "Name",1,"my_addon" }, { "AODeConstructor",AODECONSTRUCTOR_VERSION,&media_filter }, { "AOResourceAccess",AORESOURCEACCESS_VERSION,&resource_access }, { 0,0,0 }, };
We use the #ifdef in order to build a shared (DLL) and static (library) version of our addon with the same source code. This way we can link directly with the static version if we want our application to be completely self contained, and use the DLL if not.
To use the our addon's resources in an application, we simply get the addon's AOResourceAccess interface using the AOGetInterface() function, call its GetResources() function, and iterate through the resources until we find one we want to look at. If we want to change one of the resources, and its AOR_TYPE_WRITABLE type flag is set, we call the interfaces SetResource() function. Here are examples for getting and setting the volume:
int32_t GetVolume(AOICtrl_t *ctrl,void *ctx) { AOResource_t *res; AOInterface_t *i; // does it have resource access? if (i=AOGetInterface(ctrl,"AOResourceAccess",AORESOURCEACCESS_VERSION,0)) { // does it have resources? if (res=i->GetResources(ctx)) { // iterate through the resources for (;res->name;res++) { // is the current resource the volume? if (strcmp(res->name,"Volume")==0) return *((int32_t*)res->value); } } } return -1; } int32_t SetVolume(AOICtrl_t *ctrl,void *ctx,int32_t volume) { AOInterface_t *i; // does it have resource access? if (i=AOGetInterface(ctrl,"AOResourceAccess",AORESOURCEACCESS_VERSION,0)) { // try to set its Volume resource. return i->SetResource(ctx,"Volume",&volume); } return -1; }