Controlling the IMedia Interface
With the IMedia interface created and the media loaded, it's time to begin actually using the media interface. Most operations on IMedia directly impact playback, such as starting and stopping playback, although that's not always the case. For example, if you're playing video, you might want to grab frames of the video directly. As you might imagine, this is an asynchronous event. To do this, you might add the following to your IMedia callback:
void IMediaEventHandler(void *pUser, AEEMediaCmdNotify *pCmdNotify)
pApp *pMe = (pApp*)pUser;
if( pCmdNotify->nStatus == MM_MOVIE_LOADED )
if(pCmdNotify->nCmd == MM_CMD_PLAY)
//.....Draw the IBitmap to the screen
As you can see, the code for getting a frame from a movie is fairly simple, and resides almost entirely in the IMedia callback.
Unfortunately, the status code returned by BREW when media loading has completed prior to fetching frames isn't defined in all versions of the SDK, so you begin by defining an undocumented status code that is sent to the event handler when the movie is loaded.
| Author's Note: On most phones I've worked with, the code is 0x105, but your mileage may vary; be prepared to output all the status commands to the BREW logger during a movie load and playback and watch for hex values greater than 100.
Next, use the same basic outline for a callback handler showed in the previous example. First, reclaim the app pointer and declare a bitmap pointer to contain the resulting frame. Next, look for media loaded value to indicate that the media has been loaded by the IMedia interface. When it arrives, you'll know that the movie is buffered and ready in the IMedia object. Then, to get the individual frames from a movie, you'll need to enable the frame callback using IMEDIA_EnableFrameCallback. With frame-playback enabled, call play and wait for the frames to start coming in.
When the frame is ready, you receive a Play command with the status equal to MM_PARM_FRAME. During that callback it's safe to call IMEDIA_GetFrame, which allocates and returns an IBitmap object. Next, simply draw the Bitmap to the screen and exit. Of course, you can always simply fetch the first frame, draw the first frame and pause, or perform other transformations such as scaling on the resulting bitmap.
Of course, playing movies does not require using the frame callback. In fact, once the movie is loaded, you can call IMEDIA_SetRect to set a destination rectangle for the movie and IMEDIA_Play, which will render each frame within the rectangle you specify; the IMedia object takes care of the rest.
Most playback control is typically driven by a combination of user events (the user triggers a playback pause by pressing a key, for example) and programmatic events (a game might loop a soundtrack during the help screen). Consequently, the logic behind how to control media playback is domain-dependent, but the IMedia interfaces you use aren't. For example, an audio or video player might have the following code in the application's event handler when playback is in process:
#define JUMP_TIME 500
switch( wParam )
IMEDIA_Seek( pThis->pIMedia, MM_SEEK_CURRENT , JUMP_TIME);
IMEDIA_Seek(pThis->pIMedia, MMS_SEEK_CURRENT, -(JUMP_TIME));
if (pThis->bPlaying )
pThis->bPlaying = FALSE;
IMEDIA_Pause( pIMedia );
pThis->bPlaying = TRUE;
IMEDIA_Resume( pIMedia );
Of course, this code should only be executed with an active media player during playback. It simply maps the left and right keys to skipping 500 ms behind or ahead in the media, and the select key to toggling playback between pause and playing states.
One thing to remember when using Imedia: many codecs won't behave well if you attempt to play media before it's fully loaded, an asynchronous operation. Always wait for either a keypress to start playback with IMEDIA_Play, or look for a media loaded event in your callback--or at least attempt to begin playback asynchronously using ISHELL_Resume and a callback function, verifying the result code from IMEDIA_Play. Otherwise, playback may not start on some handsets.
Cleaning Up After IMedia
Because IMedia is just a BREW interface like any other, cleanup is simply a matter of unregistering your callback function and releasing the resources used by IMedia:
IMEDIA_RegisterNotify(pThis->pIMedia, NULL, NULL);
While probably not strictly necessary, it's always a good idea to clear your callback function before releasing your IMedia object.
While on the topic of cleaning up after IMedia, I've found that trying to reuse the same IMedia interface across different pieces of media data can cause unpredictable results. Some handsets appear to get confused and play the same data regardless of the source data passed to an IMedia interface. Because creating a new IMedia interface is relatively inexpensive, I recommend always releasing an IMedia interface and obtaining a new one if you want to render a different multimedia file.
Handling Suspend and Resume Events
About the only other thing you must worry about when working with IMedia is what to do with BREW's suspend and resume events. At the very least, you should pause playback of media on receiving a suspend event using IMEDIA_Pause, and resume playback using IMEDIA_Resume. This will keep the media codec from using memory, CPU, and potentially both audio and video hardware while your application is suspended, which you must do if you want to pass True BREW certification. If it's appropriate, it's definitely worth considering releasing the IMedia interface on suspend and creating a new one on resume, because you can offer the underlying system more resources that way.
It's So Easy!
Although the documentation for IMedia can look overwhelming to the developer new to BREW, it's not difficult, as you now know. Using IMedia on BREW is easy: simply create an instance of IMedia using IMediaUtil, set a callback, and invoke IMEDIA_Play.