# Class MediaManager

java.lang.Object
com.codename1.media.MediaManager

public abstract class MediaManager extends Object

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

- ## Constructor Summary

  Constructors
  Constructor
  Description
  `MediaManager()`

- ## Method Summary

  Modifier and Type
  Method
  Description
  `static void`
  `addCompletionHandler(Media media, Runnable onCompletion)`
  Adds a callback to a Media element that will be called when the media finishes playing.
  `static Media`
  `createBackgroundMedia(String uri)`
  Creates an audio media that can be played in the background.
  `static AsyncResource<Media>`
  `createBackgroundMediaAsync(String uri)`
  Creates an audio media asynchronously that can be played in the background.
  `static SoundPoolPeer`
  `createFallbackSoundPoolPeer(int maxStreams)`
  Returns the cross platform `MediaManager` based `SoundPoolPeer` fallback, used by `com.codename1.gaming.SoundPool` when the platform provides no native low latency backend.
  `static Media`
  `createMedia(InputStream stream, String mimeType)`
  Creates the Media in the given stream.
  `static Media`
  `createMedia(InputStream stream, String mimeType, Runnable onCompletion)`
  Creates the Media in the given stream Notice that you should invoke cleanup on a media once you are done with it.
  `static Media`
  `createMedia(String uri, boolean isVideo)`
  Creates a Media from a given URI
  `static Media`
  `createMedia(String uri, boolean isVideo, Runnable onCompletion)`
  Creates a Media from a given URI
  `static AsyncResource<Media>`
  `createMediaAsync(InputStream stream, String mimeType, Runnable onCompletion)`
  Creates the Media in the given stream asynchronously.
  `static AsyncResource<Media>`
  `createMediaAsync(String uri, boolean isVideo, Runnable onCompletion)`
  Creates a Media from a given URI asynchronously.
  `static Media`
  `createMediaRecorder(MediaRecorderBuilder builder)`
  Creates a Media recorder Object which will record from the device mic to a file in the given path.
  `static Media`
  `createMediaRecorder(String path)`
  Creates a Media recorder Object which will record from the device mic to a file in the given path.
  `static Media`
  `createMediaRecorder(String path, String mimeType)`
  Creates a Media recorder Object which will record from the device mic to a file in the given path.
  `static void`
  `deleteAudioBuffer(String path)`
  Deletes the audio buffer at the given path.
  `static AsyncMedia`
  `getAsyncMedia(Media media)`
  Converts the media object into an AsyncMedia object.
  `static AudioBuffer`
  `getAudioBuffer(String path)`
  Gets an audio buffer at the given path.
  `static AudioBuffer`
  `getAudioBuffer(String path, boolean create, int size)`
  Gets or creates an audio buffer at the given path.
  `static String[]`
  `getAvailableRecordingMimeTypes()`
  Gets the available recording MimeTypes
  `static String`
  `getMediaRecorderingMimeType()`
  Gets the recording mime type for the returned Media from the createMediaRecorder method
  `static RemoteControlListener`
  `getRemoteControlListener()`
  Gets the currently registered remote control listener.
  `static void`
  `releaseAudioBuffer(String path)`
  Releases an audio buffer at a given path.
  `static void`
  `removeCompletionHandler(Media media, Runnable onCompletion)`
  Removes onComplete callback from Media element.
  `static void`
  `setRemoteControlListener(RemoteControlListener l)`
  Registers a listener to be notified of remote control events - e.g.

  ### Methods inherited from class Object

  `clone, equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- ## Constructor Details

  - ### MediaManager

    public MediaManager()

- ## Method Details

  - ### getAudioBuffer

    public static AudioBuffer getAudioBuffer(String path)

    Gets an audio buffer at the given path.

    ###### Parameters

    - `path`: @param path 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

    The AudioBuffer or null if no buffer exists at that path.

  - ### getAudioBuffer

    public static AudioBuffer getAudioBuffer(String path, boolean create, int size)

    Gets or creates an audio buffer at the given path.

    ###### Parameters

    - `path`: @param path 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.

    - `create`: @param create If this flag is true and no buffer exists at the given path, then the buffer will be created.

    - `size`: The maximum size of the buffer.

    ###### Returns

    The audio buffer or null if no buffer exists at that path and the create flag is false.

  - ### releaseAudioBuffer

    public static void releaseAudioBuffer(String path)

    Releases an audio buffer at a given path. Audio buffers use a simple reference counter mechanism. Every call to `boolean, int)` will increment the counter, and calls to `#releaseAudioBuffer(java.lang.String)` will decrement the counter.

    ###### Parameters

    - `path`: The path to the buffer.

  - ### deleteAudioBuffer

    public static void deleteAudioBuffer(String path)

    Deletes the audio buffer at the given path.

    ###### Parameters

    - `path`: The path to the audio buffer to delete.

    ###### Deprecated

    Prefer to use `#releaseAudioBuffer(java.lang.String)`

  - ### getRemoteControlListener

    public static RemoteControlListener getRemoteControlListener()

    Gets the currently registered remote control listener.

    ###### Returns

    Returns:
      The currently registered remote control listener, or null if none is registered.

  - ### setRemoteControlListener

    public static void setRemoteControlListener(RemoteControlListener l)

    Registers a listener to be notified of remote control events - e.g. the play/pause/seek buttons on the user's lock screen when background media is being played.

    ###### Parameters

    - `l`: The remote control listener to set. null to set no listener.

  - ### createBackgroundMedia

    public static Media createBackgroundMedia(String uri) throws IOException

    Creates an audio media that can be played in the background.

    ###### Parameters

    - `uri`: @param uri the uri of the media can start with jar://, file://, http:// (can also use rtsp:// if supported on the platform)

    ###### Returns

    Returns:

      Media a Media Object that can be used to control the playback of the media

      ###### Throws

      - `IOException`: if creation of media from the given URI has failed
    Throws:
      `IOException`

  - ### createBackgroundMediaAsync

    public static AsyncResource<Media> createBackgroundMediaAsync(String uri)

    Creates an audio media asynchronously that can be played in the background.

    ###### Parameters

    - `uri`: @param uri the uri of the media can start with jar://, file://, http:// (can also use rtsp:// if supported on the platform)

    ###### Returns

    Returns:
      Media a Media Object that can be used to control the playback of the media

  - ### createMedia

    public static Media createMedia(String uri, boolean isVideo) throws IOException

    Creates a Media from a given URI

    ###### Parameters

    - `uri`: @param uri the uri of the media can start with file://, http:// (can also use rtsp:// although may not be supported on all target platforms)

    - `isVideo`: a boolean flag to indicate if this is a video media

    ###### Returns

    Returns:

      Media a Media Object that can be used to control the playback of the media

      ###### Throws

      - `IOException`: if creation of media from the given URI has failed
    Throws:
      `IOException`

  - ### createMedia

    public static Media createMedia(InputStream stream, String mimeType) throws IOException

    Creates the Media in the given stream. Notice that you should invoke cleanup on a media once you are done with it.

    ###### Parameters

    - `stream`: the stream containing the media data

    - `mimeType`: the type of the data in the stream

    ###### Returns

    Returns:

      Media a Media Object that can be used to control the playback of the media

      ###### Throws

      - `java.io.IOException`: if the creation of the Media has failed
    Throws:
      `IOException`

  - ### createMediaAsync

    public static AsyncResource<Media> createMediaAsync(InputStream stream, String mimeType, Runnable onCompletion)

    Creates the Media in the given stream asynchronously. Notice that you should invoke cleanup on a media once you are done with it.

    ###### Parameters

    - `stream`: the stream containing the media data

    - `mimeType`: the type of the data in the stream

    ###### Returns

    Returns:
      Media a Media Object that can be used to control the playback of the media

  - ### createMedia

    public static Media createMedia(String uri, boolean isVideo, Runnable onCompletion) throws IOException

    Creates a Media from a given URI

    ###### Parameters

    - `uri`: @param uri the uri of the media can start with file://, http:// (can also use rtsp:// although may not be supported on all target platforms)

    - `isVideo`: a boolean flag to indicate if this is a video media

    - `onCompletion`: a Runnable to be called when the media has finished

    ###### Returns

    Returns:

      Media a Media Object that can be used to control the playback of the media

      ###### Throws

      - `IOException`: if creation of media from given URI failed
    Throws:
      `IOException`

  - ### createMediaAsync

    public static AsyncResource<Media> createMediaAsync(String uri, boolean isVideo, Runnable onCompletion)

    Creates a Media from a given URI asynchronously.

    ###### Parameters

    - `uri`: @param uri the uri of the media can start with file://, http:// (can also use rtsp:// although may not be supported on all target platforms)

    - `isVideo`: a boolean flag to indicate if this is a video media

    - `onCompletion`: a Runnable to be called when the media has finished

    ###### Returns

    Returns:
      Media a Media Object that can be used to control the playback of the media

  - ### addCompletionHandler

    public static void addCompletionHandler(Media media, Runnable onCompletion)

    Adds a callback to a Media element that will be called when the media finishes playing.

    ###### Parameters

    - `media`: The media to add the callback to.

    - `onCompletion`: The callback that will run on the EDT when the playback completes.

    ###### See also

    - #removeCompletionHandler(com.codename1.media.Media, java.lang.Runnable)

  - ### removeCompletionHandler

    public static void removeCompletionHandler(Media media, Runnable onCompletion)

    Removes onComplete callback from Media element.

    ###### Parameters

    - `media`: The media element.

    - `onCompletion`: The callback.

    ###### See also

    - #addCompletionHandler(com.codename1.media.Media, java.lang.Runnable)

  - ### createMedia

    public static Media createMedia(InputStream stream, String mimeType, Runnable onCompletion) throws IOException

    Creates the Media in the given stream Notice that you should invoke cleanup on a media once you are done with it.

    ###### Parameters

    - `stream`: the stream containing the media data

    - `mimeType`: the type of the data in the stream

    - `onCompletion`: a Runnable to be called when the media has finished

    ###### Returns

    Returns:

      Media a Media Object that can be used to control the playback of the media

      ###### Throws

      - `java.io.IOException`: if the URI access fails
    Throws:
      `IOException`

  - ### createMediaRecorder

    public static Media createMediaRecorder(String path) throws IOException

    Creates a Media recorder Object which will record from the device mic to a file in the given path. The output format will be amr-nb if supported by the platform.

    ###### Parameters

    - `path`: @param path a file path to where to store the recording, if the file does not exists it will be created.

    ###### Deprecated

    see createMediaRecorder(String path, String mimeType) instead

    Throws:
      `IOException`

  - ### getMediaRecorderingMimeType

    public static String getMediaRecorderingMimeType()

    Gets the recording mime type for the returned Media from the createMediaRecorder method

    ###### Returns

    the recording mime type

    ###### Deprecated

    see getAvailableRecordingMimeTypes() instead

  - ### getAvailableRecordingMimeTypes

    public static String[] getAvailableRecordingMimeTypes()
    Gets the available recording MimeTypes

  - ### createMediaRecorder

    public static Media createMediaRecorder(String path, String mimeType) throws IOException

    Creates a Media recorder Object which will record from the device mic to a file in the given path.

    ###### Parameters

    - `path`: @param path a file path to where to store the recording, if the file does not exists it will be created.

    - `mimeType`: @param mimeType 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

    Throws:
      `IOException`

  - ### createMediaRecorder

    public static Media createMediaRecorder(MediaRecorderBuilder builder) throws IOException

    Creates a Media recorder Object which will record from the device mic to a file in the given path.

    ###### Parameters

    - `builder`: media settings

    ###### Throws

    - `IllegalArgumentException`: if given mime-type is not supported

    - `IOException`: id failed to create a Media object

    Throws:
      `IOException`

  - ### getAsyncMedia

    public static AsyncMedia getAsyncMedia(Media media)

    Converts the media object into an AsyncMedia object. Many media objects area already instances of AsyncMedia, so this method would perform a simple cast. For media objects that are not already async, this will return an Async wrapper.

    ###### Parameters

    - `media`: The media object to convert.

    ###### Returns

    The media object as an AsyncMedia instance.

  - ### createFallbackSoundPoolPeer

    public static SoundPoolPeer createFallbackSoundPoolPeer(int maxStreams)

    Returns the cross platform `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

    - `maxStreams`: the maximum number of simultaneously playing voices