Client API. The Codename One framework your app is built on: this runs on the device, not in a backend.
public abstract class MediaManager
- Object
- MediaManager
Allow us to create Media objects using String URI’s or with a stream to
the media data.
Notice that the underlying platforms contains the actual codecs, therefore you need to play common media file types such as mp3, mp4 to successfully play them across devices on all target platforms. The simulator can’t accurately reproduce the behavior of this class in all devices/cases.
The sample code below demonstrates simple video playback.
final Form hi = new Form("MediaPlayer", new BorderLayout());
hi.setToolbar(new Toolbar());
Style s = UIManager.getInstance().getComponentStyle("Title");
FontImage icon = FontImage.createMaterial(FontImage.MATERIAL_VIDEO_LIBRARY, s);
hi.getToolbar().addCommandToRightBar(new Command("", icon) {
@Override
public void actionPerformed(ActionEvent evt) {
Display.getInstance().openGallery((e) -> {
if(e != null && e.getSource() != null) {
String file = (String)e.getSource();
try {
Media video = MediaManager.createMedia(file, true);
hi.removeAll();
hi.add(BorderLayout.CENTER, new MediaPlayer(video));
hi.revalidate();
} catch(IOException err) {
Log.e(err);
}
}
}, Display.GALLERY_VIDEO);
}
});
hi.show();
The code below demonstrates capturing audio using the Capture API and playing back audio files using the Media API:
Form hi = new Form("Capture", BoxLayout.y());
hi.setToolbar(new Toolbar());
Style s = UIManager.getInstance().getComponentStyle("Title");
FontImage icon = FontImage.createMaterial(FontImage.MATERIAL_MIC, s);
FileSystemStorage fs = FileSystemStorage.getInstance();
String recordingsDir = fs.getAppHomePath() + "recordings/";
fs.mkdir(recordingsDir);
try {
for(String file : fs.listFiles(recordingsDir)) {
MultiButton mb = new MultiButton(file.substring(file.lastIndexOf("/") + 1));
mb.addActionListener((e) -> {
try {
Media m = MediaManager.createMedia(recordingsDir + file, false);
m.play();
} catch(IOException err) {
Log.e(err);
}
});
hi.add(mb);
}
hi.getToolbar().addCommandToRightBar("", icon, (ev) -> {
try {
String file = Capture.captureAudio();
if(file != null) {
SimpleDateFormat sd = new SimpleDateFormat("yyyy-MMM-dd-kk-mm");
String fileName =sd.format(new Date());
String filePath = recordingsDir + fileName;
Util.copy(fs.openInputStream(file), fs.openOutputStream(filePath));
MultiButton mb = new MultiButton(fileName);
mb.addActionListener((e) -> {
try {
Media m = MediaManager.createMedia(filePath, false);
m.play();
} catch(IOException err) {
Log.e(err);
}
});
hi.add(mb);
hi.revalidate();
}
} catch(IOException err) {
Log.e(err);
}
});
} catch(IOException err) {
Log.e(err);
}
hi.show();
The code below demonstrates capturing audio and playing back audio using the Media, MediaManager and MediaRecorderBuilder APIs, as alternative and more customizable approach than using the Capture API:
private static final EasyThread countTime = EasyThread.start("countTime");
public void start() {
if (current != null) {
current.show();
return;
}
Form hi = new Form("Recording audio", BoxLayout.y());
hi.add(new SpanLabel("Example of recording and playback audio using the Media, MediaManager and MediaRecorderBuilder APIs"));
hi.add(recordAudio((String filePath) -> {
ToastBar.showInfoMessage("Do something with the recorded audio file: " + filePath);
}));
hi.show();
}
public static Component recordAudio(OnComplete callback) {
try {
// mime types supported by Android: audio/amr, audio/aac, audio/mp4
// mime types supported by iOS: audio/mp4, audio/aac, audio/m4a
// mime type supported by Simulator: audio/wav
// more info: https://www.iana.org/assignments/media-types/media-types.xhtml
List availableMimetypes = Arrays.asList(MediaManager.getAvailableRecordingMimeTypes());
String mimetype;
if (availableMimetypes.contains("audio/aac")) {
// Android and iOS
mimetype = "audio/aac";
} else if (availableMimetypes.contains("audio/wav")) {
// Simulator
mimetype = "audio/wav";
} else {
// others
mimetype = availableMimetypes.get(0);
}
String fileName = "audioExample." + mimetype.substring(mimetype.indexOf("/") + 1);
String output = FileSystemStorage.getInstance().getAppHomePath() + "/" + fileName;
// https://tritondigitalcommunity.force.com/s/article/Choosing-Audio-Bitrate-Settings
MediaRecorderBuilder options = new MediaRecorderBuilder()
.mimeType(mimetype)
.path(output)
.bitRate(64000)
.samplingRate(44100);
Media[] microphone = {MediaManager.createMediaRecorder(options)};
Media[] speaker = {null};
Container recordingUI = new Container(BoxLayout.y());
Label time = new Label("0:00");
Button recordBtn = new Button("", FontImage.MATERIAL_FIBER_MANUAL_RECORD, "Button");
Button playBtn = new Button("", FontImage.MATERIAL_PLAY_ARROW, "Button");
Button stopBtn = new Button("", FontImage.MATERIAL_STOP, "Button");
Button sendBtn = new Button("Send");
sendBtn.setEnabled(false);
Container buttons = GridLayout.encloseIn(3, recordBtn, stopBtn, sendBtn);
recordingUI.addAll(FlowLayout.encloseCenter(time), FlowLayout.encloseCenter(buttons));
recordBtn.addActionListener(l -> {
try {
// every time we have to create a new instance of Media to make it working correctly (as reported in the Javadoc)
microphone[0] = MediaManager.createMediaRecorder(options);
if (speaker[0] != null && speaker[0].isPlaying()) {
return; // do nothing if the audio is currently recorded or played
}
recordBtn.setEnabled(false);
sendBtn.setEnabled(true);
Log.p("Audio recording started", Log.DEBUG);
if (buttons.contains(playBtn)) {
buttons.replace(playBtn, stopBtn, CommonTransitions.createEmpty());
buttons.revalidateWithAnimationSafety();
}
if (speaker[0] != null) {
speaker[0].pause();
}
microphone[0].play();
startWatch(time);
} catch (IOException ex) {
Log.p("ERROR recording audio", Log.ERROR);
Log.e(ex);
}
});
stopBtn.addActionListener(l -> {
if (!microphone[0].isPlaying() && (speaker[0] == null || !speaker[0].isPlaying())) {
return; // do nothing if the audio is NOT currently recorded or played
}
recordBtn.setEnabled(true);
sendBtn.setEnabled(true);
Log.p("Audio recording stopped");
if (microphone[0].isPlaying()) {
microphone[0].pause();
} else if (speaker[0] != null) {
speaker[0].pause();
} else {
return;
}
stopWatch(time);
if (buttons.contains(stopBtn)) {
buttons.replace(stopBtn, playBtn, CommonTransitions.createEmpty());
buttons.revalidateWithAnimationSafety();
}
if (FileSystemStorage.getInstance().exists(output)) {
Log.p("Audio saved to: " + output);
} else {
ToastBar.showErrorMessage("Error recording audio", 5000);
Log.p("ERROR SAVING AUDIO");
}
});
playBtn.addActionListener(l -> {
// every time we have to create a new instance of Media to make it working correctly (as reported in the Javadoc)
if (microphone[0].isPlaying() || (speaker[0] != null && speaker[0].isPlaying())) {
return; // do nothing if the audio is currently recorded or played
}
recordBtn.setEnabled(false);
sendBtn.setEnabled(true);
if (buttons.contains(playBtn)) {
buttons.replace(playBtn, stopBtn, CommonTransitions.createEmpty());
buttons.revalidateWithAnimationSafety();
}
if (FileSystemStorage.getInstance().exists(output)) {
try {
speaker[0] = MediaManager.createMedia(output, false, () -> {
// callback on completation
recordBtn.setEnabled(true);
if (speaker[0].isPlaying()) {
speaker[0].pause();
}
stopWatch(time);
if (buttons.contains(stopBtn)) {
buttons.replace(stopBtn, playBtn, CommonTransitions.createEmpty());
buttons.revalidateWithAnimationSafety();
}
});
speaker[0].play();
startWatch(time);
} catch (IOException ex) {
Log.p("ERROR playing audio", Log.ERROR);
Log.e(ex);
}
}
});
sendBtn.addActionListener(l -> {
if (microphone[0].isPlaying()) {
microphone[0].pause();
}
if (speaker[0] != null && speaker[0].isPlaying()) {
speaker[0].pause();
}
if (buttons.contains(stopBtn)) {
buttons.replace(stopBtn, playBtn, CommonTransitions.createEmpty());
buttons.revalidateWithAnimationSafety();
}
stopWatch(time);
recordBtn.setEnabled(true);
callback.completed(output);
});
return FlowLayout.encloseCenter(recordingUI);
} catch (IOException ex) {
Log.p("ERROR recording audio", Log.ERROR);
Log.e(ex);
return new Label("Error recording audio");
}
}
private static void startWatch(Label label) {
label.putClientProperty("stopTime", Boolean.FALSE);
countTime.run(() -> {
long startTime = System.currentTimeMillis();
while (label.getClientProperty("stopTime") == Boolean.FALSE) {
// the sleep is every 200ms instead of 1000ms to make the app more reactive when stop is tapped
Util.sleep(200);
int seconds = (int) ((System.currentTimeMillis() - startTime) / 1000);
String min = (seconds / 60) + "";
String sec = (seconds % 60) + "";
if (sec.length() == 1) {
sec = "0" + sec;
}
String newTime = min + ":" + sec;
if (!label.getText().equals(newTime)) {
CN.callSerially(() -> {
label.setText(newTime);
if (label.getParent() != null) {
label.getParent().revalidateWithAnimationSafety();
}
});
}
}
});
}
private static void stopWatch(Label label) {
label.putClientProperty("stopTime", Boolean.TRUE);
}
Constructors
public MediaManager() |
Methods
Inherited methods
Constructor details
MediaManager
public MediaManager()Method details
getAudioBuffer
public static AudioBuffer getAudioBuffer(String path)Parameters
pathString- The path to the Audio buffer. This path doesn’t correspond to a real file. It is just used as a key to map to the audio buffer so that it can be addressed.
Returns
getAudioBuffer
public static AudioBuffer getAudioBuffer(String path, boolean create, int size)Parameters
pathString- The path to the Audio buffer. This path doesn’t correspond to a real file. It is just used as a key to map to the audio buffer so that it can be addressed.
createboolean- If this flag is true and no buffer exists at the given path, then the buffer will be created.
sizeint- The maximum size of the buffer.
Returns
releaseAudioBuffer
public static void releaseAudioBuffer(String path)boolean, int) will increment
the counter, and calls to #releaseAudioBuffer(java.lang.String) will decrement the counter.Parameters
pathString- The path to the buffer.
deleteAudioBuffer
public static void deleteAudioBuffer(String path)#releaseAudioBuffer(java.lang.String)Parameters
pathString- The path to the audio buffer to delete.
getRemoteControlListener
public static RemoteControlListener getRemoteControlListener()Returns
setRemoteControlListener
public static synchronized void setRemoteControlListener(RemoteControlListener l)Parameters
lRemoteControlListener- The remote control listener to set. null to set no listener.
createBackgroundMedia
public static Media createBackgroundMedia(String uri)
throws IOExceptionParameters
uriString- the uri of the media can start with jar://, file://, http:// (can also use rtsp:// if supported on the platform)
Returns
Throws
IOException- if creation of media from the given URI has failed
createBackgroundMediaAsync
public static AsyncResource<Media> createBackgroundMediaAsync(String uri)Parameters
uriString- the uri of the media can start with jar://, file://, http:// (can also use rtsp:// if supported on the platform)
Returns
createMedia
public static Media createMedia(String uri, boolean isVideo)
throws IOExceptionParameters
uriString- the uri of the media can start with file://, http:// (can also use rtsp:// although may not be supported on all target platforms)
isVideoboolean- a boolean flag to indicate if this is a video media
Returns
Throws
IOException- if creation of media from the given URI has failed
createMedia
public static Media createMedia(InputStream stream, String mimeType)
throws IOExceptionParameters
streamInputStream- the stream containing the media data
mimeTypeString- the type of the data in the stream
Returns
Throws
java.io.IOException- if the creation of the Media has failed
createMediaAsync
public static AsyncResource<Media> createMediaAsync(InputStream stream, String mimeType, Runnable onCompletion)Parameters
streamInputStream- the stream containing the media data
mimeTypeString- the type of the data in the stream
onCompletionRunnable- Not documented.
Returns
createMedia
public static Media createMedia(String uri, boolean isVideo, Runnable onCompletion)
throws IOExceptionParameters
uriString- the uri of the media can start with file://, http:// (can also use rtsp:// although may not be supported on all target platforms)
isVideoboolean- a boolean flag to indicate if this is a video media
onCompletionRunnable- a Runnable to be called when the media has finished
Returns
Throws
IOException- if creation of media from given URI failed
createMediaAsync
public static AsyncResource<Media> createMediaAsync(String uri, boolean isVideo, Runnable onCompletion)Parameters
uriString- the uri of the media can start with file://, http:// (can also use rtsp:// although may not be supported on all target platforms)
isVideoboolean- a boolean flag to indicate if this is a video media
onCompletionRunnable- a Runnable to be called when the media has finished
Returns
addCompletionHandler
public static void addCompletionHandler(Media media, Runnable onCompletion)Parameters
mediaMedia- The media to add the callback to.
onCompletionRunnable- The callback that will run on the EDT when the playback completes.
removeCompletionHandler
public static void removeCompletionHandler(Media media, Runnable onCompletion)Parameters
mediaMedia- The media element.
onCompletionRunnable- The callback.
createMedia
public static Media createMedia(InputStream stream, String mimeType, Runnable onCompletion)
throws IOExceptionParameters
streamInputStream- the stream containing the media data
mimeTypeString- the type of the data in the stream
onCompletionRunnable- a Runnable to be called when the media has finished
Returns
Throws
java.io.IOException- if the URI access fails
createMediaRecorder
public static Media createMediaRecorder(String path)
throws IOExceptionParameters
pathString- a file path to where to store the recording, if the file does not exists it will be created.
Throws
getMediaRecorderingMimeType
public static String getMediaRecorderingMimeType()Returns
getAvailableRecordingMimeTypes
public static String[] getAvailableRecordingMimeTypes()createMediaRecorder
public static Media createMediaRecorder(String path, String mimeType)
throws IOExceptionParameters
pathString- a file path to where to store the recording, if the file does not exists it will be created.
mimeTypeString- the output mime type that is supported see getAvailableRecordingMimeTypes()
Throws
IllegalArgumentException- if given mime-type is not supported
IOException- id failed to create a Media object
createMediaRecorder
public static Media createMediaRecorder(MediaRecorderBuilder builder)
throws IOExceptionParameters
builderMediaRecorderBuilder- media settings
Throws
IllegalArgumentException- if given mime-type is not supported
IOException- id failed to create a Media object
getAsyncMedia
public static AsyncMedia getAsyncMedia(Media media)Parameters
mediaMedia- The media object to convert.
Returns
createFallbackSoundPoolPeer
public static SoundPoolPeer createFallbackSoundPoolPeer(int maxStreams)MediaManager based SoundPoolPeer fallback,
used by com.codename1.gaming.SoundPool when the platform provides no native
low latency backend. Applications should use com.codename1.gaming.SoundPool
rather than calling this directly.Parameters
maxStreamsint- the maximum number of simultaneously playing voices