# Interface Media

All Known Subinterfaces:
  `AsyncMedia`

All Known Implementing Classes:
  `AbstractMedia`

public interface Media

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

- MediaManager

- ## Field Summary

  Fields
  Modifier and Type
  Field
  Description
  `static final String`
  `VARIABLE_ANDROID_SEEK_PREVIEW_WORKAROUND`
  Optional boolean flag to enable an Android-specific workaround for flaky black-frame previews after `setTime(int)` on paused video.
  `static final String`
  `VARIABLE_BACKGROUND_ALBUM_COVER`
  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
  `static final String`
  `VARIABLE_BACKGROUND_ARTIST`
  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
  `static final String`
  `VARIABLE_BACKGROUND_DURATION`
  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
  `static final String`
  `VARIABLE_BACKGROUND_POSITION`
  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
  `static final String`
  `VARIABLE_BACKGROUND_SUPPORTED`
  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.
  `static final String`
  `VARIABLE_BACKGROUND_TITLE`
  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
  `static final String`
  `VARIABLE_NATIVE_CONTRLOLS_EMBEDDED`
  Write-only variable that can be used with getVariable() to set whether this video should include embedded native controls.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `void`
  `cleanup()`
  Stops the audio playback and cleans up the resources related to it immediately.
  `int`
  `getDuration()`
  Returns the length in milliseconds of the audio file or -1 if not known
  `int`
  `getTime()`
  Returns the time in milliseconds in the audio file or -1 if not known
  `Object`
  `getVariable(String key)`
  Allows querying platform specific information from the media object
  `Component`
  `getVideoComponent()`
  Gets the VideoComponent of this Video.
  `int`
  `getVolume()`
  Returns the media playback volume in percentage
  `boolean`
  `isFullScreen()`
  This method returns true if this video is in full screen mode.
  `boolean`
  `isNativePlayerMode()`
  Returns true if this Video Media is in Native player mode.
  `boolean`
  `isPlaying()`
  Returns true if the media is currently playing or recording
  `boolean`
  `isVideo()`
  This method returns true if this is a Video Media
  `void`
  `pause()`
  Pauses (actually stops) the playback or the recording of the media file
  `void`
  `play()`
  Starts playing or recording the media file
  `void`
  `prepare()`
  Optional call that allows the caller to prepare the upcoming media player.
  `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
  `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.
  `void`
  `setTime(int time)`
  Sets the position in the audio file or doesn't effect if not supported
  `void`
  `setVariable(String key, Object value)`
  Allows for platform specific enhancements for media playback
  `void`
  `setVolume(int vol)`
  Sets the media playback volume in percentage

- ## Field Details

  - ### VARIABLE_BACKGROUND_TITLE

    static final String VARIABLE_BACKGROUND_TITLE
    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
    See Also:
      - Constant Field Values

  - ### VARIABLE_BACKGROUND_ARTIST

    static final String VARIABLE_BACKGROUND_ARTIST
    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
    See Also:
      - Constant Field Values

  - ### VARIABLE_BACKGROUND_DURATION

    static final String VARIABLE_BACKGROUND_DURATION
    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
    See Also:
      - Constant Field Values

  - ### VARIABLE_BACKGROUND_ALBUM_COVER

    static final String VARIABLE_BACKGROUND_ALBUM_COVER
    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
    See Also:
      - Constant Field Values

  - ### VARIABLE_BACKGROUND_POSITION

    static final String VARIABLE_BACKGROUND_POSITION
    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
    See Also:
      - Constant Field Values

  - ### VARIABLE_BACKGROUND_SUPPORTED

    static final String VARIABLE_BACKGROUND_SUPPORTED
    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.
    See Also:
      - Constant Field Values

  - ### VARIABLE_NATIVE_CONTRLOLS_EMBEDDED

    static final String VARIABLE_NATIVE_CONTRLOLS_EMBEDDED
    Write-only variable that can be used with getVariable() to set whether this video should include embedded native controls.
    See Also:
      - Constant Field Values

  - ### VARIABLE_ANDROID_SEEK_PREVIEW_WORKAROUND

    static final String VARIABLE_ANDROID_SEEK_PREVIEW_WORKAROUND
    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.

    See Also:
      - Constant Field Values

- ## Method Details

  - ### play

    void play()
    Starts playing or recording the media file

  - ### pause

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

  - ### prepare

    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

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

  - ### getTime

    int getTime()

    Returns the time in milliseconds in the audio file or -1 if not known

    ###### Returns

    time in milliseconds

  - ### setTime

    void setTime(int time)

    Sets the position in the audio file or doesn't effect if not supported

    ###### Parameters

    - `time`: in milliseconds

  - ### getDuration

    int getDuration()

    Returns the length in milliseconds of the audio file or -1 if not known

    ###### Returns

    time in milliseconds

  - ### getVolume

    int getVolume()

    Returns the media playback volume in percentage

    ###### Returns

    the volume percentage

  - ### setVolume

    void setVolume(int vol)

    Sets the media playback volume in percentage

    ###### Parameters

    - `vol`: the volume for media playback

  - ### isPlaying

    boolean isPlaying()

    Returns true if the media is currently playing or recording

    ###### Returns

    true if playing

  - ### getVideoComponent

    Component getVideoComponent()

    Gets the VideoComponent of this Video.

    ###### Returns

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

  - ### isVideo

    boolean isVideo()

    This method returns true if this is a Video Media

    ###### Returns

    true if video

  - ### isFullScreen

    boolean isFullScreen()

    This method returns true if this video is in full screen mode.

    ###### Returns

    true if full screen

  - ### setFullScreen

    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

    ###### Parameters

    - `fullScreen`

  - ### isNativePlayerMode

    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

    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

    void setVariable(String key, Object value)

    Allows for platform specific enhancements for media playback

    ###### Parameters

    - `key`: the key to set to the media that is platform specific

    - `value`: the value

  - ### getVariable

    Object getVariable(String key)

    Allows querying platform specific information from the media object

    ###### Parameters

    - `key`: the key query

    ###### Returns

    the value or null