Creating and Configuring the IMedia Interface
Before anything else can happen, you must first have an instance to an IMedia subclass for your data. While in some applications you might know ahead of time which IMedia class you must create, it's often just easiest to use the IMediaUtil
class to do the work of content type recognition, IMedia instance creation, and passing your multimedia data to the newly created IMedia instance:
char szFilename = "TestMedia.mp3";
ISHELL_CreateInstance(pIShell, AEECLSID_MEDIAUTIL, (void **)&pMediaUtil);
sMediaData.clsData = MMD_FILE_NAME;
sMediaData.pData = (void *)& szFilename;
sMediaData.dwSize = 0;
IMEDIAUTIL_CreateMedia(pMediaUtil, &sMediaData, &pIMediaObject);
(void *) pMe);
Up first are object definitions. The variables pMediaUtil
are the two classes I create through BREW's ISHELL_CreateInstance
interface. Next is sMediaData
, the structure used to obtain the correct subclass of the IMedia object. Finally, there is szFilename
, the name of the media to play.
First, create an instance of IMediaUtil using ISHELL_CreateInstance, checking to ensure the creation succeeds. Once you have your MediaUtil object, you need to build the AEEMediaData structure that describes your media and its source. While IMedia and IMediaUtil can take media data from a file, memory region, or an ISource, using the file system (via the specifier MMD_FILE_NAME) has been the most reliable method on BREW handsets to date. Of course, you can use the method that best fits your design, but don't expect all three to work on every phone.
Now that you've filled in the MediaData structure, you can pass that information into a IMEDIAUTIL_CreateMedia call. In turn, the IMediaUtil allocates the correct media object and fills that data into the pIMediaObject pointer. In addition, by using CreateMedia, you've not only created the correct IMedia subclass, but you've also started the process of loading the media data into the seleted codec.
After creating your IMedia object, you can release the MediaUtil.
Finally, because nearly all of IMedia's function API's are asynchronous, you need to give your IMedia object a way to communicate with your application. The IMEDIA_RegisterNotify provides the IMedia object with a function to call passing status updates.
An example of an IMedia method that returns its value through the callback is IMEDIA_GetTotalTime: it won't respond right away with the length of the file, but it will call your callback as soon as the media has been loaded.
As you might imagine, the callback you register can get pretty complicated. In sample code it's reasonable to have business logic for your application in the callback, but, for a production application, it's often clearer to restrict the callback to a switch/case statement that calls appropriate routines for each notification. The sample code looks like this:
void IMediaEventHandler(void *pUser, AEEMediaCmdNotify *pCmdNotify)
pApp *pMe = (pApp*)pUser;
if(pCmdNotify->nStatus == MM_STATUS_DONE)
pMe->dwTotalMediaTime = (int)pCmdNotify->pData;
if( SUCCESS != IMEDIA_Play(pCmdNotify->pIMedia) )
//Set a timer to attempt the play later
//Change UI to reflect play status
//Change UI to reflect stop status
//Advance your time counter here
This is about the simplest IMedia callback function you might encouter. It begins by reclaiming the application's state pointer from the incoming voice pointer, and then declares a rectangle that you can use later.
As was just noted, IMEDIA_GetTotalTime returns its response asynchronously through the callback: it calls the IMediaEventHandler asynchronously. As with all notifications, the incoming AEEMediaCmdNotify includes both the command generating the notification and any arguments; you simply decode this using a switch statement and take appropriate action. For example, when pCmdNotify->nCmd is MM_CMD_GETTOTALTIME, you know both that your media has been loaded and pCmdNotify->pData will be the length of that media in seconds.
Although successful IMedia invocations result in your callback being called, failed ones do not. It's important to remember that anytime you call an IMedia method, always check the return value. At any point, the IMedia object can return EBADSTATE or EITEMBUSY (even in response to the most basic function calls). Be prepared for this by being ready to repeat any command at a later time when the media object isn't busy or in a bad state.
This callback function also monitors the status of the MM_CMD_PLAY command over time to keep the UI up do date on the media object's progress. This is a good way to trigger animations when playing audio, updating a status bar, and so forth.