We’ve recently released a few updates to our Media APIs in order to support a wider variety of use cases. One of these upgrades is the new AsyncMedia
interface, which includes async play and pause support.
The Media
interface has always had play()
and pause()
methods, but it didn’t provide an easy way to detect when they had taken effect. On most platforms, this hasn’t been a major issue because the delay between when play()
is called, and when the media actually starts playing is small, or negligible. But our Javascript port presented some new challenges due to strict browser permissions surrounding media playback. In some cases, which I’ll discuss in detail in my next blog post, the user will be prompted to provide permission to play audio, or access the microphone, when play()
is called. This means that the delay between play()
and when the media actually starts playing may be significant.
New AsyncMedia interface
The new AsyncMedia
interface extends Media
and adds some new methods. Among these new methods, you’ll find playAsync()
and pauseAsync()
which return PlayRequest
and PauseRequest
objects respectively.
Since MediaManager
returns media as Media
objects, you’ll need to convert them into AsyncMedia
objects in order to access the new functionality. You can use the new MediaManager.getAsyncMedia(Media)
method for this purpose. Almost all Media
objects in Codename One already implement AsyncMedia
, so the getAsyncMedia()
will usually just return the same object, but casted to AsyncMedia
. In the rare case where the Media
object, doesn’t already implement AsyncMedia
, getAsyncMedia()
will return a proxy wrapper around the original media object, that includes support for the new Async APIs.
Let’s look at an example:
import static com.codename1.media.MediaManager.*;
import com.codename1.media.*;
//...
boolean playPending, playing;
// ...
AsyncMedia media = getAsyncMedia(createMedia(mediaUrl, false));
playPending = true;
media.playAsync().ready(m->{
playPending = false;
Playing = true;
}).except(ex->{
playPending = false;
Dialog.show("Failed to play", "Failed to play media: "+ex.getMessage(), "OK", null);
});
media.addMediaStateChangeListener(evt->{
Playing = (evt.getNewState() == State.Playing);
});
This code is a complete example of how you can keep track of the playing state of your media. It keeps track of both whether there is a play request pending, and whether it is currently playing. Technically you don’t need to keep your own variable for keeping track of the the “playing” state, as you can just call media.isPlaying()
, or media.getState()
at any time. I use a separate playing variable here just to illustrate how to synchronize your application state with the state of your media.
How it works
playAsync()
returns a PlayRequest
object, which is a subclass of AsyncResource<Media>
. Its “ready” callback will be executed once playing has begun. The “except” callback will be called if playback fails due to an error.
You can also use the mediaStateChangeListeners of the AsyncMedia
object to keep track of changes to state. Whenever the media starts playing, or stops playing, it will fire one of these events.
The new pauseAsync()
method works similarly.
Check out the AsyncMediaSample sample in the Samples project for a full working example that makes use of playAsync()
.