public interface Media

Known subtypesAbstractMedia, AsyncMedia

Media control interface allows for media playback, recording. To get an instance of this interface (implemented by the native port) see the MediaManager class.

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);
    }

See also

Fields

public static final String VARIABLE_BACKGROUND_TITLE = "bgTitle"Write only variable that can be used with setVariable to pass a title for the native layer so the currently playing media title will be displayed in the lock screen where applicable
public static final String VARIABLE_BACKGROUND_ARTIST = "bgArtist"Write only variable that can be used with setVariable to pass the artist name to the native layer so the currently playing media title will be displayed in the lock screen where applicable
public static final String VARIABLE_BACKGROUND_DURATION = "bgDuration"Write only variable that can be used with setVariable to pass the duration for the media as a Long object to the native layer so the currently playing media title will be displayed in the lock screen where applicable
public static final String VARIABLE_BACKGROUND_ALBUM_COVER = "bgCover"Write only variable that can be used with setVariable to pass the album cover Image to the native layer so the currently playing media title will be displayed in the lock screen where applicable
public static final String VARIABLE_BACKGROUND_POSITION = "bgPosition"Write only variable that can be used with setVariable to pass the position in the media (Long object) to the native layer so the currently playing media title will be displayed in the lock screen where applicable
public static final String VARIABLE_BACKGROUND_SUPPORTED = "bgInfoSupported"Read only variable that can be used with getVariable to query whether the native layer supports displaying the currently playing media information in the lock screen.
public static final String VARIABLE_NATIVE_CONTRLOLS_EMBEDDED = "nativeControlsVisible"Write-only variable that can be used with getVariable() to set whether this video should include embedded native controls.
public static final String VARIABLE_ANDROID_SEEK_PREVIEW_WORKAROUND = "androidSeekPreviewWorkaround"Optional boolean flag to enable an Android-specific workaround for flaky black-frame previews after setTime(int) on paused video.

Methods

public abstract void play()Starts playing or recording the media file
public abstract void pause()Pauses (actually stops) the playback or the recording of the media file
public abstract void prepare()Optional call that allows the caller to prepare the upcoming media player.
public abstract void cleanup()Stops the audio playback and cleans up the resources related to it immediately.
public abstract int getTime()Returns the time in milliseconds in the audio file or -1 if not known
public abstract void setTime(int time)Sets the position in the audio file or doesn’t effect if not supported
public abstract int getDuration()Returns the length in milliseconds of the audio file or -1 if not known
public abstract int getVolume()Returns the media playback volume in percentage
public abstract void setVolume(int vol)Sets the media playback volume in percentage
public abstract boolean isPlaying()Returns true if the media is currently playing or recording
public abstract Component getVideoComponent()Gets the VideoComponent of this Video.
public abstract boolean isVideo()This method returns true if this is a Video Media
public abstract boolean isFullScreen()This method returns true if this video is in full screen mode.
public abstract void setFullScreen(boolean fullScreen)Sets the Media to be displayed full screen, make sure the getVideoComponent() is called on the Video Component is added to the current Form
public abstract boolean isNativePlayerMode()Returns true if this Video Media is in Native player mode.
public abstract void setNativePlayerMode(boolean nativePlayer)By calling this the Media (if it’s a Video) will be played full screen on the native Player of the device.
public abstract void setVariable(String key, Object value)Allows for platform specific enhancements for media playback
public abstract Object getVariable(String key)Allows querying platform specific information from the media object

Field details

VARIABLE_BACKGROUND_TITLE

public static final String VARIABLE_BACKGROUND_TITLE = "bgTitle"
Write only variable that can be used with setVariable to pass a title for the native layer so the currently playing media title will be displayed in the lock screen where applicable

VARIABLE_BACKGROUND_ARTIST

public static final String VARIABLE_BACKGROUND_ARTIST = "bgArtist"
Write only variable that can be used with setVariable to pass the artist name to the native layer so the currently playing media title will be displayed in the lock screen where applicable

VARIABLE_BACKGROUND_DURATION

public static final String VARIABLE_BACKGROUND_DURATION = "bgDuration"
Write only variable that can be used with setVariable to pass the duration for the media as a Long object to the native layer so the currently playing media title will be displayed in the lock screen where applicable

VARIABLE_BACKGROUND_ALBUM_COVER

public static final String VARIABLE_BACKGROUND_ALBUM_COVER = "bgCover"
Write only variable that can be used with setVariable to pass the album cover Image to the native layer so the currently playing media title will be displayed in the lock screen where applicable

VARIABLE_BACKGROUND_POSITION

public static final String VARIABLE_BACKGROUND_POSITION = "bgPosition"
Write only variable that can be used with setVariable to pass the position in the media (Long object) to the native layer so the currently playing media title will be displayed in the lock screen where applicable

VARIABLE_BACKGROUND_SUPPORTED

public static final String VARIABLE_BACKGROUND_SUPPORTED = "bgInfoSupported"
Read only variable that can be used with getVariable to query whether the native layer supports displaying the currently playing media information in the lock screen. This will return null or Boolean.TRUE.

VARIABLE_NATIVE_CONTRLOLS_EMBEDDED

public static final String VARIABLE_NATIVE_CONTRLOLS_EMBEDDED = "nativeControlsVisible"
Write-only variable that can be used with getVariable() to set whether this video should include embedded native controls.

VARIABLE_ANDROID_SEEK_PREVIEW_WORKAROUND

public static final String VARIABLE_ANDROID_SEEK_PREVIEW_WORKAROUND = "androidSeekPreviewWorkaround"

Optional boolean flag to enable an Android-specific workaround for flaky black-frame previews after setTime(int) on paused video.

The default is false. When enabled, Android may perform an additional internal refresh seek to force a frame redraw without changing behaviour on other platforms.

Method details

play

public abstract void play()
Starts playing or recording the media file

pause

public abstract void pause()
Pauses (actually stops) the playback or the recording of the media file

prepare

public abstract void prepare()

Optional call that allows the caller to prepare the upcoming media player. This is useful when streaming multiple streams one after another.

Note: On some platforms (iOS), the poster frame and native embedded controls will not appear for the video until you call this method, otherwise.

cleanup

public abstract void cleanup()
Stops the audio playback and cleans up the resources related to it immediately.

getTime

public abstract int getTime()
Returns the time in milliseconds in the audio file or -1 if not known

Returns

time in milliseconds

setTime

public abstract void setTime(int time)
Sets the position in the audio file or doesn’t effect if not supported

Parameters

time int
in milliseconds

getDuration

public abstract int getDuration()
Returns the length in milliseconds of the audio file or -1 if not known

Returns

time in milliseconds

getVolume

public abstract int getVolume()
Returns the media playback volume in percentage

Returns

the volume percentage

setVolume

public abstract void setVolume(int vol)
Sets the media playback volume in percentage

Parameters

vol int
the volume for media playback

isPlaying

public abstract boolean isPlaying()
Returns true if the media is currently playing or recording

Returns

true if playing

getVideoComponent

public abstract Component getVideoComponent()
Gets the VideoComponent of this Video.

Returns

a Component of the video to be placed on a Form or null if this Media is not a Video

isVideo

public abstract boolean isVideo()
This method returns true if this is a Video Media

Returns

true if video

isFullScreen

public abstract boolean isFullScreen()
This method returns true if this video is in full screen mode.

Returns

true if full screen

setFullScreen

public abstract void setFullScreen(boolean fullScreen)
Sets the Media to be displayed full screen, make sure the getVideoComponent() is called on the Video Component is added to the current Form

isNativePlayerMode

public abstract boolean isNativePlayerMode()
Returns true if this Video Media is in Native player mode. Some platforms such as BlackBerry is able to play video only on the native player on those platforms isNativePlayerMode() will always return true If Media supports native playing by calling to play() the video will start playing in the native player in full screen.

Returns

true if the player is in native mode

setNativePlayerMode

public abstract void setNativePlayerMode(boolean nativePlayer)
By calling this the Media (if it’s a Video) will be played full screen on the native Player of the device. Native playing assumes getVideoComponent() hasn’t been called on this Media unexpected behaviour may occur if getVideoComponent() has been called and the Component is placed on a Form. Some platforms such as BlackBerry is able to play video only on the native player on those platforms isNativePlayerMode() will always return true

setVariable

public abstract void setVariable(String key, Object value)
Allows for platform specific enhancements for media playback

Parameters

key String
the key to set to the media that is platform specific
value Object
the value

getVariable

public abstract Object getVariable(String key)
Allows querying platform specific information from the media object

Parameters

key String
the key query

Returns

the value or null