Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Java Development

Everything You Want To Know About Multimedia, But Don't.

by Retired ‎03-05-2012 11:37 AM - edited ‎07-09-2012 10:35 AM (6,302 Views)

This article is intended for software developers and as such is pretty technical in nature.  It is assumed that readers have at least a novice understanding of Java® and the BlackBerry® platform in general.  In addition, it is assumed that readers have at least looked at the API’s referenced.

This article is not meant to be an introduction to the MMAPI, in fact it is assumed that readers have some familiarity with the API already.  Instead topics are covered where the API specification is vague or open to interpretation, so that developers can understand how the platform works in these circumstances.  MMAPI also supports adding platform-specific extensions to the API, and the BlackBerry extensions to the API are also documented and expanded on here.  This article will jump around a few topics at times so it won't always make sense reading it from top to bottom, but that is also by design so you can jump to the topic you are interested in.

I apologize in advance for the length, but I figure it's better to have the information and not use it.  And without further ado...



There are three ways to create a Player object for media playback, one being to use a locator String.  This is also the only way to create a Player for recording media.  Locators are well-formed URI’s that follow a specific format as outlined in the MMAPI Player JavaDoc™, however not all valid parameters for locator strings are used in the BlackBerry implementation.  The list of supported parameters is found in Table 1 below, including the version of BlackBerry OS that support was added, if applicable.  Parameters that are not supported are ignored by the implementation; however supported parameters with unsupported or invalid values will cause a MediaException.  Parameters are generally case-sensitive, however values generally are not.


Valid Values

Support Added


Any valid content-type for the platform



Any valid rate for the encoding specified. Applies only when voipMode=true is also used.

OS 6.1+


Any positive integer.  Applies to video only



Any positive integer.  Applies to video only


Table 1: Valid Locator Parameters


The following table 2 describes locator parameters specific to the BlackBerry platform.  Note that the value for “mms” specifies video optimized for mms messaging.  Specifically this means a video size of 176 x 144.  Standard is the same except the size is specified as 640 x 480.



Valid Values

Support Added


Any of "AMR", "AAC", "QCELP".  Applies to video recording only.

"NONE" was added in OS 7.0+

OS 5.0+


Any of "H263", "H264", or "MPEG-4".  Applies to video recording only.

OS 5.0+


Any positive integer.  Applies to video recording only.

OS 6.1+


Either “mms” or “standard”

OS 5.0+


“true” or “false”

OS 6.0+

Table 2: BlackBerry Specific Locator Parameters


Player States Explained

If you’ve already checked out the MMAPI JavaDocs, then you’re probably already familiar with the Player State Diagram.   But it’s important to understand, so it’s worth showing it again here.


Player State Diagram 



Figure 1: The MMAPI Player State Diagram

Source: MMAPI JavaDocs http://download.oracle.com/javame/config/cldc/opt-pkgs/api/mm/jsr135/index.html


This is all good and well, but what do the states actually mean?  Is there a difference between REALIZED and PREFETCHED?  Well, here’s what you’re dying to know.


An UNREALIZED player is the state of a player when it is first created.  Call it a default state, you can’t really do anything with it – you can’t get any of its controls, you can’t call most methods on it, and it has not been initialized in any way.

By calling the realize() method, the player becomes REALIZED.  A player in the REALIZED state means that it has done everything it needs to do in order to begin playback except acquire any scarce resources needed.  On a BlackBerry, this means that you can now obtain and use all of the players’ controls including the VolumeControl and the VideoControl.  You can also call VideoControl.initDisplayMode() and setup the UI for playback in your application, and obtain the AudioPathControl to set the audio path prior to playback, for example.

Recall that on CDMA devices there is a single audio channel.  This is considered a scarce resource so it is not yet acquired by your player in the REALIZED state.  Acquiring this resource will effectively ‘steal’ it from any other application that is currently using it, therefore if you don’t intend to start playback for a while it is best to do any setup of your player in the REALIZED state.

The PREFETCHED state is in every way the same as the REALIZED state, with the only difference being any scarce resources are also acquired with the intent that playback is imminent.  On a CDMA BlackBerry this means that any other player is stopped and your PREFETCHED player is allowed to start.  On a GSM® BlackBerry this means that your player is allowed to start and will play over any other playing sounds at the same time.  In all cases this also means that while transitioning to PREFETCHED the BlackBerry OS has tried to open the media and has read and buffered data in preparation for playback.

That last point deserves some elaboration.  If you have created your player using a file or http URL String locator the BlackBerry OS will try to open the file at the location specified and read from it, so it must be accessible when the player becomes PREFETCHED.  If you have created your player using a custom DataSource, the BlackBerry OS may call read() and/or seek() on it to start reading data in the same manner.  If your DataSource is abstracting a remote file, this is an important case your application needs to handle.  See the DataSources section below for complete details on what this means for your application.

When you no longer need the player you created, it is always important to call close() on it.  This will make sure any resources used by the player are cleaned up properly.  Once CLOSED, you cannot call any method on your player again as doing so will throw an IllegalStateException.  If the player was created using a custom DataSource, then BlackBerry OS 5.0 and previous will automatically close() the DataSource as well once the player is closed.  BlackBerry OS 6.0 and above will not automatically close the DataSource.  This was done since many application developers wanted to re-use their DataSource objects.

As a final note about player states, the BlackBerry platform will automatically perform state transitions for you if desired.  Specifically, any call to move the player into a later state will automatically cause the platform to call the previous state transition methods for you.  For example, if start() is called on a player in the UNREALIZED state, realize() and prefetch() will be called in that order first, automatically.  Similarly if prefetch() is called on an UNREALIZED player, then realize() will automatically be invoked for your application before the prefetching is performed.  The most common scenario for an application is to call start() on a REALIZED player when it is ready to be started, since typically a realized player is needed to access it’s controls but since the prefetched state isn’t particularly useful to an application it doesn’t need to explicitly call prefetch().

As you have probably guessed by now, if close() is called on a PREFETCHED or STARTED player, then stop() and/or deallocate() are automatically called for your application as applicable.


Getting Video Snapshots

The VideoControl.getSnaphot() method is not universally supported for every kind of player on the BlackBerry platform.  It is supported on any player created to playback video media; it is not supported when recording video media.  Note that here I am talking about capturing snapshots of video, and not from the device camera.


When looking at the playback case, it can be somewhat ‘finicky’ to figure out.  The key is that it only works when the player is STARTED and is visible on the screen.  All calls to getSnapshot() in any other manner will result in a black screen snapshot (or more specifically all-zero bitmap data).


The byte array returned from the call to getSnapshot()contains data in Little-Endian byte-order.  In order to use this with the Bitmap class available on the BlackBerry platform this data must be converted to Big-Endian byte-order first.  Here is an example method you can use that will do this for you:


import javax.microedition.media.Player;
import javax.microedition.media.control.VideoControl;

import net.rim.device.api.system.Bitmap;

 * Gets a snapshot of any playing video.  The video MUST be
 * visible on the screen and started to work!
 * @param player The Player to acquire the snapshot from.
 * @return A Bitmap that is the snapshot, or null if one
 *         could not be obtained.
public Bitmap getSnapshot(Player player) {
    Bitmap bitmap = null;

    // Only works on a STARTED Player...
    if (player != null && player.getState() == Player.STARTED) {
        VideoControl videoControl = (VideoControl) player.getControl( "VideoControl" );

        if (videoControl != null) {
            // ...that is visible on the screen
            byte[] rgb565Data = videoControl.getSnapshot( null );

            if (rgb565Data != null) {
                int videoWidth = videoControl.getSourceWidth();
                int videoHeight = videoControl.getSourceHeight();

                bitmap = new Bitmap(Bitmap.ROWWISE_16BIT_COLOR, videoWidth, videoHeight );

                // Swap 16-bit short data to big endian byte ordering
                for (int i=0; i<rgb565Data.length; i+=2) {
                    byte temp = rgb565Data[i];
                    rgb565Data[i] = rgb565Data[i+1];
                    rgb565Data[i+1] = temp;

                bitmap.setRGB565(rgb565Data, 0, videoWidth << 1, 0, 0, videoWidth, videoHeight );

    return bitmap;


An alternative approach to capture a video snapshot is to use the Display.screenshot method.  This has the same requirement that video is visible on the screen, but it will also allow the ability to capture thumbnails while video playback is paused, and even while recording video in a viewfinder.  This can be achieved in only a few lines of code:


int width = videoControl.getSourceWidth();
int height = videoControl.getSourceHeight();

Bitmap bitmap = new Bitmap(Bitmap.ROWWISE_16BIT_COLOR, width, height );
Display.screenshot( bitmap, 0, 0, width, height );


One of the caveats to this approach is that since this method works by grabbing screenshots, you need to make sure your video is visible and that the coordinates you provide (I used 0,0 above) match where your video/viewfinder is on the screen.  Using the VideoControl ensures the width and height match exactly the dimensions of the video.  Also the resulting size of the image is the same as the size of the screen, which is why I am referring to these snapshots as 'thumbnails' - they won't necessarily be the same size as the source video.


This approach works well on BlackBerry OS 6.0+ while both playing and stopped and also on devices running BlackBerry OS 5.0, but only when the video is paused.  The only other caveat is that IT policies can restrict the ability to take screenshots so this approach may not be universally available.

Player Events


The MMAPI player events are documented in the MMAPI PlayerListener JavaDocs.  Rather than repeat that information here, I will focus on the BlackBerry-specific notes for these events.


You will always get a STOPPED event whenever END_OF_MEDIA is reached. You won’t always get a STOPPED event with an ERROR event, but you will always get one with an ERROR event if the Player was playing media. You can get an ERROR event for example if you try to seek before starting the player and that fails - in this case you won’t get a STOPPED event because it wasn’t STARTED.

Data Access From Certain Player Events

As of BlackBerry® 6.0+, a change was implemented with PlayerListeners such that the playerUpdate method is now called from a different process than the application that caused the event.  This change only affects the net.rim.device.internal.media.recordCommitted event specifically.  To understand what the change is, let’s take a step back for a moment.

The BlackBerry JVM® has a quirk that can create situations that mean the JVM doesn’t always do what you would expect it to.  This isn’t specific to the BlackBerry JVM implementation; rather it’s more about how the J2ME specification is written and what it allows.  J2ME allows JVM’s to only require one process and run all ‘Java Processes’ in that single process, working to make it appear that Java applications are running in their own processes when they actually are not.  Unlike J2SE and J2EE where new process are run in separate JVM instances, this was allowed for J2ME JVM implementations because J2ME is designed to run in resource-constrained environments, and this allows the J2ME platform to not have to incur the overhead of a whole JVM for every application process on the device.

Recall that event callbacks always occur in separate threads.  One of the consequences of the J2ME JVM design is that sometimes a Java application can call into a JVM method and wait for an event - in our case, a method that would cause a PlayerListener event to occur.  The PlayerListener event then may or may not end up getting executed in the same process as the Java Application, and as a result different threads within the same application may observe different values for the same variables even in the presence of proper synchronization!  In this case they are technically observing different variables entirely since each process has their own variables and values, the JVM is making it look like they are in the same process using the same variables when they really are not.

Prior to BlackBerry 6.0 the net.rim.device.internal.media.recordCommitted event was run in a new thread within the same application process.  From BlackBerry 6.0 and on, this event is now executed in a separate process.  To get around this, applications can use the RuntimeStore class to store and retrieve Java Objects across processes, which you can read about with examples in the BlackBerry 6.0 JavaDocs.


This particular event is posted when an update to the playable streams found in the loaded media occurs.  When this event is received, the eventData parameter will be an Integer object designating the streams that are available in the media.


The integer value is composed of two bit-masks representing the streams.  The low order 16 bits contain the playable streams, and the high order 16 bits contain the available streams (i.e. present in the container but not necessarily playable).

The bit-masks are defined as follows:

public static final int TYPE_AUDIO = 1;

public static final int TYPE_VIDEO = 2;

public static final int TYPE_TEXT  = 4;


Using this information, you can derive what types of media streams are available vs. playable by the BlackBerry device you are using.  As an example, suppose the value returned was the integer 196610.  What this really means is that the media that was loaded by the BlackBerry OS was found to contain both an AUDIO stream, and a VIDEO stream, but only the AUDIO is of a supported format and can be played by the platform.  The reason is:

196610 = 0000000000000011 0000000000000010

 Where:  0000000000000011 = 1 + 2 + 0 = 3 (Audio + Video)

   And:  0000000000000010 = 0 + 1 + 0 = 2 (Video)


Using the same example, an application can apply the bit-masks to figure this out:

int streamsMask = (Integer) eventData.intValue();
int availableStreams = streamsMask >> 16;    // High Order Bits
int playableStreams = streamsMask && 0x00FF; // Low Order Bits

// Check for available audio or video:
if ( (availableStreams & TYPE_AUDIO) || (availableStreams & TYPE_VIDEO) ) {
    // ...

// Check for playable audio or video:
if ( (playableStreams & TYPE_AUDIO) || (playableStreams & TYPE_VIDEO) ) {
    // ...


Posted when the type of a Bluetooth® AVRCP volume control changes.  This may occur when switching from a local sink (headset, handset, loudspeaker) to a remote sink (currently BlueTooth only).  The eventData parameter that accompanies the com.rim.volume.control.type event is a String that has three possible values:

  • volume.control.type.none - Indicates the player is connected to an audio sink which is not capable of volume control
  • volume.control.type.relative - Indicates the player is connected to an audio sink which is capable of increasing and decreasing volume but is not able to provide or set its’ absolute volume level
  • volume.control.type.absolute - Indicates the player is connected to an audio sink which is capable of increasing and decreasing volume as well as provide and set its’ absolute volume level



Applications can pass data to a Player using DataSource objects.  There are a few things you should know about how the BlackBerry platform uses DataSources.

The BlackBerry platform will automatically call the connect() method on any DataSource when a player is created using it via a call to Manager.createPlayer(DataSource).  The platform will also call start() on it and will acquire it’s SourceStreams[] before returning a Player from the Manager to the application.  The platform only ever assumes there is one SourceStream, and always assumes (and requires) that it is at the first index in the returned array of SourceStreams.  

If the media content-type is “audio/basic”, there will be an attempt to read the first 24-byte header when the Player is created, otherwise the DataSource is not be accessed until the Player becomes REALIZED.

In BlackBerry 6.0+, the platform will not call stop() or disconnect() on DataSource objects when the player becomes CLOSED.  In BlackBerry OS versions prior to 6.0, stop() then disconnect() is called automatically when the player becomes CLOSED.  This change was made to allow applications to more conveniently re-use DataSource objects if they so choose.  If a player is playing MIDI content using a DataSource, then in all BlackBerry OS versions stop() will not be called but disconnect() will be called when the player becomes CLOSED.


Applications can implement a SourceStream to stream data to a Player for playback.  There are a few things you should know about how the BlackBerry platform uses SourceStreams.  But first, let’s look at the concept of seeking.  For most people this may be elementary, but it’s important to fully understand the concept.

There are three levels of ‘seekable’ as defined in MMAPI – not seekable, seekable to start, and random-accessible.  If you imagine a pointer into the source stream that keeps track of a byte-offset into the stream, then a non-seekable stream implies that the pointer must start at offset 0, can only be advanced to the next byte in the order that the bytes appear, and can never be reset to any other position.  Using the same analogy then, a seekable-to-start stream is the same as a non-seekable stream with the difference being that the pointer can be reset to (only) offset 0 at any time, as often as needed.  A random-accessible stream means that the byte offset pointer can be moved to any offset required within the stream, at any time and for as many times as needed.

The BlackBerry platform will call getSeekType() on the source stream to tell which of the three the application stream is.  The value an application chooses to return will depend both on what it can support and what codec it is using.  Note that seek() doesn’t mean what most people think it means!  More on that in the More On Seeking section below.

When a player becomes REALIZED, the SourceStream will be accessed to parse metadata from it by calling read() and possibly seek().  This is necessary because any metadata information must be made available for the players’ MetaDataControl.  When applications are using a SourceStream to abstract the fact that the data is remote, this access for metadata can sometimes produce counter-intuitive behaviour.  For example, in the case of AAC and MP3 media that use ID3 for their metadata scheme, the BlackBerry OS will look for ID3v2 data at the start of the stream.  If no metadata is found however, then an attempt is made to look for ID3v1 data which is defined to exist at the end of the stream and can only be up to 128 bytes long.  This means that the BlackBerry OS will attempt to seek() to the last 128 bytes, but only if the SourceStream is RANDOM_ACCESSIBLE.  If the stream is being sourced from a network or remote location, this can result in undesirable the behaviour that most of the file must be streamed just to REALIZE the player!

When a player becomes PREFETCHED the BlackBerry platform will attempt to begin reading from the SourceStream to start buffering playback.  This may result in read and/or seek calls to the SourceStream, and the Players' behaviour from here largely depends on the media format and whether the stream is ‘seekable’ or not.

When the platform calls seek() the return value is defined to be the new offset into the stream, whether that’s the requested offset or the applications’ best-effort to get to the offset.  On the BlackBerry platform, if the return value is not the requested offset, an error will result.  Whether the source stream’s new offset is actually at the requested location is up to the application, but most of the time the platform is seeking because it needs something at that offset, so there is no guarantee playback will be successful if it is not.

Note that it is also possible to create a Player instance with an InputStream and a content-type String instead of a DataSource.  Applications will often wrap the InputStream and override certain methods including read() and markSupported().  This is perfectly acceptable, but applications do need to be aware that returning a value of true for the markSupported() method will force the BlackBerry OS to treat the InputStream as random-accessible (and conversely to treat the stream as non-seekable if false is returned).  If this is not the case, then there may be issues during playback.

More On Seeking

Seeking on a SourceStream doesn’t mean what most people think it means.  Most application developers assume that a seek() call is how the BlackBerry platform moves forward or backward (e.g. like moving the scroll bar in the BlackBerry media player) while playing media.  While true, it is only part of the story.

The easiest explanation is to look at an example.  Consider my favourite example, the AVI file.  In order to play AVI files the index block must be read in order to obtain the offsets into the frame data for each audio and video frame (I will likely make a separate post with more details about AVI files later).  Since the index block is at the end of the file, this means that the platform may do one of two things, depending on the seek type:

If the seek type is SourceStream.RANDOM_ACCESSIBLE, then it will call seek() in order to jump to the offset of the index block, read it, then call seek() again to jump back.  However, if the seek type is either SourceStream.NOT_SEEKABLE or SourceStream.SEEKABLE_TO_START then the previous method is not possible.  Instead the platform will send a PlayerListener.ERROR event, with a return code of 10, which according to the PlayerListener.ERROR JavaDoc documentation means  “The media player needs to seek in the file in order to access headers, but can't since the file being played is unseekable”.  If the application were streaming the AVI data over the network, this of course means that it would need to download nearly the entire file before playback can even begin, which is most likely very undesirable.  On the other hand, if the application is getting the data from a local AVI file, then this is definitely possible to do.

The example above, as well as the metadata example talked about earlier, shows how the decision of seek type not only depends on the data in the stream (which is defined by the choice of codec), but also where the data is coming from, and the choice of seek type can greatly influence what the platform will do.  In general, when streaming data from remote sources, RANDOM_ACCESSIBLE source streams and “markSupported” input streams are rarely advisable.  In the case of SourceStreams, SEEKABLE_TO_START for calls to getSeekType() are recommended instead.  Conversely, for local files RANDOM_ACCESSIBLE streams are highly advised since performance of the platform can be greatly optimized in many cases.

Handling Media Interruption

The standard mechanism used when media must be interrupted is by the PlayerListener.DEVICE_UNAVAILABLE and PlayerListener.DEVICE_AVAILABLE events.  The former occurs when the BlackBerry platform or another 3rd-party application has prefetched a player and has thus taken the resources needed to play from your application.  The latter notifies your application that those same resources have been returned to your application so it may resume playback again.  Although I say prefetching will cause this pre-emption, you should know that I really mean prefetched or started; for brevity I’ll stick with just saying prefetched.

Your application will always receive the DEVICE_UNAVAILABLE event when resources are taken away, however you will only receive a DEVICE_AVAILABLE if the BlackBerry platform was the one to take them.  In other words, if a 3rd-party application prefetched a player that resulted in a DEVICE_UNAVAILABLE event to your player, when the player now holding resources releases them, your player will not be notified.  This is the only scenario where this is the case.


Recall that on CDMA devices there is only a single channel to play audio or video.  As soon as any other player is prefetched that new player will preempt the player that has the resources.  On GSM devices there are two channels available, but when a third player is simultaneously prefetched the behaviour is the same – one of the players holding onto the resource will be preempted and a DEVICE_UNAVAILABLE event will be sent to notify it.  Although there is no mention (and despite what I’m about to write, no guarantee), as far back as BlackBerry OS 4.2 through OS 6.1 the player that will be preempted in this GSM scenario will be the last one to have been prefetched.


Undocumented Goodies


Below is a section of undocumented API’s that may be useful for multimedia development.  Note that since they are undocumented, relying on them for your application is done at your own risk.  These API’s are subject to change/disappear/move without any notice, so if your application suddenly breaks with future BlackBerry OS releases, you’ll know why.  You’ve been warned!



There are several methods in this class available to 3rd-Parties.  Two in particular are isRecordingCodecSupported(int codec) and isCodecSupported(int codec), but unfortunately the codec values to pass into each method were never documented.  See Table 3 below for the list of valid values to pass in, and their meaning.





















Table 3: Undocumented Audio Codec Constants


Undocumented Player Events

As anyone who has worked with multimedia on the BlackBerry platform is no doubt aware, there are several undocumented events that 3rd-Party Player objects are notified of, which are documented in Table 4.  While not all of them are useful to 3rd-Parties, some may be.


Event String

Event Description


Posted when loading of media is initiated.  Basically this means that the Player is currently PREFETCHING.  No eventData is sent with this event, it is NULL.


Posted when media is loaded.  No eventData is sent with this event, it is null.  Basically this means that the Player is PREFETCHED.


Posted when an update to the seekable state of media occurs. When this event is received, the eventData parameter will be a Boolean object designating whether the media is seekable or not.


Posted when an update to the playable streams found in the loaded media occurs.  When this event is received, the eventData parameter will be an Integer object designating the streams that are available in the media.    See above for an explanation on how to decode this value.


Posted when a time tick occurs on the currently playing media. When this event is received, the eventData parameter will be an unreadable proprietary TimeEvent object that gives the current time in the media.


The TimeEvent object is not usable by applications, however the event is sent every second so it can still be used for example to implement a seek-bar or timer, as long as you know the duration of the media.  Between that and Player.getMediaTime (to tell you where you end up after a seek() call) users are able to implement all of the functionality that knowing the time in this event would tell you.


Posted when StreamingBufferControl.flush() is called. The eventData parameter will be NULL.


Posted when the type of a Bluetooth AVRCP volume control changes.  This may occur when switching from a local sink (headset, handset, loudspeaker) to a remote sink (currently BlueTooth only).  See above for an explanation on how to decode the eventData value.

Table 4: Undocumented BlackBerry Player Events

Users Online
Currently online: 21 members 1,600 guests
Please welcome our newest community members: