/*****************************************************************************
* MediaPlayer.java
*****************************************************************************
* Copyright © 2015 VLC authors and VideoLAN
*
* Authors Jean-Baptiste Kempf <jb@videolan.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
package org.videolan.libvlc;
@SuppressWarnings("unused")
public class MediaPlayer extends VLCObject<MediaPlayer.Event> {
public static class Event extends VLCEvent {
//public static final int MediaChanged = 0x100;
//public static final int NothingSpecial = 0x101;
public static final int Opening = 0x102;
//public static final int Buffering = 0x103;
public static final int Playing = 0x104;
public static final int Paused = 0x105;
public static final int Stopped = 0x106;
//public static final int Forward = 0x107;
//public static final int Backward = 0x108;
public static final int EndReached = 0x109;
public static final int EncounteredError = 0x10a;
public static final int TimeChanged = 0x10b;
public static final int PositionChanged = 0x10c;
public static final int SeekableChanged = 0x10d;
public static final int PausableChanged = 0x10e;
//public static final int TitleChanged = 0x10f;
//public static final int SnapshotTaken = 0x110;
//public static final int LengthChanged = 0x111;
public static final int Vout = 0x112;
//public static final int ScrambledChanged = 0x113;
public static final int ESAdded = 0x114;
public static final int ESDeleted = 0x115;
//public static final int ESSelected = 0x116;
private final long arg1;
private final float arg2;
protected Event(int type) {
super(type);
this.arg1 = 0;
this.arg2 = 0;
}
protected Event(int type, long arg1) {
super(type);
this.arg1 = arg1;
this.arg2 = 0;
}
protected Event(int type, float arg2) {
super(type);
this.arg1 = 0;
this.arg2 = arg2;
}
public long getTimeChanged() {
return arg1;
}
public float getPositionChanged() {
return arg2;
}
public int getVoutCount() {
return (int) arg1;
}
public int getEsChangedType() {
return (int) arg1;
}
public boolean getPausable() {
return arg1 != 0;
}
public boolean getSeekable() {
return arg1 != 0;
}
}
public interface EventListener extends VLCEvent.Listener<MediaPlayer.Event> {}
public static class Position {
public static final int Disable = -1;
public static final int Center = 0;
public static final int Left = 1;
public static final int Right = 2;
public static final int Top = 3;
public static final int TopLeft = 4;
public static final int TopRight = 5;
public static final int Bottom = 6;
public static final int BottomLeft = 7;
public static final int BottomRight = 8;
}
public static class Navigate {
public static final int Activate = 0;
public static final int Up = 1;
public static final int Down = 2;
public static final int Left = 3;
public static final int Right = 4;
}
public static class Title {
/**
* duration in milliseconds
*/
public final long duration;
/**
* title name
*/
public final String name;
/**
* true if the title is a menu
*/
public final boolean menu;
public Title(long duration, String name, boolean menu) {
this.duration = duration;
this.name = name;
this.menu = menu;
}
}
@SuppressWarnings("unused") /* Used from JNI */
private static Title createTitleFromNative(long duration, String name, boolean menu) {
return new Title(duration, name, menu);
}
public static class Chapter {
/**
* time-offset of the chapter in milliseconds
*/
public final long timeOffset;
/**
* duration of the chapter in milliseconds
*/
public final long duration;
/**
* chapter name
*/
public final String name;
private Chapter(long timeOffset, long duration, String name) {
this.timeOffset = timeOffset;
this.duration = duration;
this.name = name;
}
}
@SuppressWarnings("unused") /* Used from JNI */
private static Chapter createChapterFromNative(long timeOffset, long duration, String name) {
return new Chapter(timeOffset, duration, name);
}
public static class TrackDescription {
public final int id;
public final String name;
private TrackDescription(int id, String name) {
this.id = id;
this.name = name;
}
}
@SuppressWarnings("unused") /* Used from JNI */
private static TrackDescription createTrackDescriptionFromNative(int id, String name) {
return new TrackDescription(id, name);
}
public static class Equalizer {
@SuppressWarnings("unused") /* Used from JNI */
private long mInstance;
private Equalizer() {
nativeNew();
}
private Equalizer(int index) {
nativeNewFromPreset(index);
}
@Override
protected void finalize() throws Throwable {
try {
nativeRelease();
} finally {
super.finalize();
}
}
/**
* Create a new default equalizer, with all frequency values zeroed.
* The new equalizer can subsequently be applied to a media player by invoking
* {@link MediaPlayer#setEqualizer}.
*/
public static Equalizer create() {
return new Equalizer();
}
/**
* Create a new equalizer, with initial frequency values copied from an existing
* preset.
* The new equalizer can subsequently be applied to a media player by invoking
* {@link MediaPlayer#setEqualizer}.
*/
public static Equalizer createFromPreset(int index) {
return new Equalizer(index);
}
/**
* Get the number of equalizer presets.
*/
public static int getPresetCount() {
return nativeGetPresetCount();
}
/**
* Get the name of a particular equalizer preset.
* This name can be used, for example, to prepare a preset label or menu in a user
* interface.
*
* @param index index of the preset, counting from zero.
* @return preset name, or NULL if there is no such preset
*/
public static String getPresetName(int index) {
return nativeGetPresetName(index);
}
/**
* Get the number of distinct frequency bands for an equalizer.
*/
public static int getBandCount() {
return nativeGetBandCount();
}
/**
* Get a particular equalizer band frequency.
* This value can be used, for example, to create a label for an equalizer band control
* in a user interface.
*
* @param index index of the band, counting from zero.
* @return equalizer band frequency (Hz), or -1 if there is no such band
*/
public static float getBandFrequency(int index) {
return nativeGetBandFrequency(index);
}
/**
* Get the current pre-amplification value from an equalizer.
*
* @return preamp value (Hz)
*/
public float getPreAmp() {
return nativeGetPreAmp();
}
/**
* Set a new pre-amplification value for an equalizer.
* The new equalizer settings are subsequently applied to a media player by invoking
* {@link MediaPlayer#setEqualizer}.
* The supplied amplification value will be clamped to the -20.0 to +20.0 range.
*
* @param preamp value (-20.0 to 20.0 Hz)
* @return true on success.
*/
public boolean setPreAmp(float preamp) {
return nativeSetPreAmp(preamp);
}
/**
* Get the amplification value for a particular equalizer frequency band.
*
* @param index counting from zero, of the frequency band to get.
* @return amplification value (Hz); NaN if there is no such frequency band.
*/
public float getAmp(int index) {
return nativeGetAmp(index);
}
/**
* Set a new amplification value for a particular equalizer frequency band.
* The new equalizer settings are subsequently applied to a media player by invoking
* {@link MediaPlayer#setEqualizer}.
* The supplied amplification value will be clamped to the -20.0 to +20.0 range.
*
* @param index counting from zero, of the frequency band to set.
* @param amp amplification value (-20.0 to 20.0 Hz).
* \return true on success.
*/
public boolean setAmp(int index, float amp) {
return nativeSetAmp(index, amp);
}
private static native int nativeGetPresetCount();
private static native String nativeGetPresetName(int index);
private static native int nativeGetBandCount();
private static native float nativeGetBandFrequency(int index);
private native void nativeNew();
private native void nativeNewFromPreset(int index);
private native void nativeRelease();
private native float nativeGetPreAmp();
private native boolean nativeSetPreAmp(float preamp);
private native float nativeGetAmp(int index);
private native boolean nativeSetAmp(int index, float amp);
}
private Media mMedia = null;
private boolean mPlaying = false;
private boolean mPlayRequested = false;
private int mVoutCount = 0;
private boolean mAudioReset = false;
private String mAudioOutput = null;
private String mAudioOutputDevice = null;
private final AWindow mWindow = new AWindow(new AWindow.SurfaceCallback() {
@Override
public void onSurfacesCreated(AWindow vout) {
boolean play = false;
boolean enableVideo = false;
synchronized (MediaPlayer.this) {
if (!mPlaying && mPlayRequested)
play = true;
else if (mVoutCount == 0)
enableVideo = true;
}
if (play)
play();
else if (enableVideo)
setVideoTrackEnabled(true);
}
@Override
public void onSurfacesDestroyed(AWindow vout) {
boolean disableVideo = false;
synchronized (MediaPlayer.this) {
if (mVoutCount > 0)
disableVideo = true;
}
if (disableVideo)
setVideoTrackEnabled(false);
synchronized (MediaPlayer.this) {
/* Wait for Vout destruction (mVoutCount = 0) in order to be sure that the surface is not
* used after leaving this callback. This shouldn't be needed when using MediaCodec or
* AndroidWindow (i.e. after Android 2.3) since the surface is ref-counted */
while (mVoutCount > 0) {
try {
MediaPlayer.this.wait();
} catch (InterruptedException ignored) {
}
}
}
}
});
/**
* Create an empty MediaPlayer
*
* @param libVLC a valid libVLC
*/
public MediaPlayer(LibVLC libVLC) {
nativeNewFromLibVlc(libVLC, mWindow);
}
/**
* Create a MediaPlayer from a Media
*
* @param media a valid Media object
*/
public MediaPlayer(Media media) {
if (media == null || media.isReleased())
throw new IllegalArgumentException("Media is null or released");
mMedia = media;
mMedia.retain();
nativeNewFromMedia(mMedia, mWindow);
}
/**
* Get the IVLCVout helper.
*/
public IVLCVout getVLCVout() {
return mWindow;
}
/**
* Set a Media
*
* @param media a valid Media object
*/
public void setMedia(Media media) {
if (media != null) {
if (media.isReleased())
throw new IllegalArgumentException("Media is released");
media.setDefaultMediaPlayerOptions();
}
nativeSetMedia(media);
synchronized (this) {
if (mMedia != null) {
mMedia.release();
}
if (media != null)
media.retain();
mMedia = media;
}
}
/**
* Get the Media used by this MediaPlayer. This Media should be released with {@link #release()}.
*/
public synchronized Media getMedia() {
if (mMedia != null)
mMedia.retain();
return mMedia;
}
/**
* Play the media
*
*/
public void play() {
synchronized (this) {
if (!mPlaying) {
/* HACK: stop() reset the audio output, so set it again before first play. */
if (mAudioReset) {
if (mAudioOutput != null)
nativeSetAudioOutput(mAudioOutput);
if (mAudioOutputDevice != null)
nativeSetAudioOutputDevice(mAudioOutputDevice);
mAudioReset = false;
}
mPlayRequested = true;
if (mWindow.areSurfacesWaiting())
return;
}
mPlaying = true;
}
nativePlay();
}
/**
* Stops the playing media
*
*/
public void stop() {
synchronized (this) {
mPlayRequested = false;
mPlaying = false;
mAudioReset = true;
}
nativeStop();
}
/**
* Set if, and how, the video title will be shown when media is played
*
* @param position see {@link Position}
* @param timeout
*/
public void setVideoTitleDisplay(int position, int timeout) {
nativeSetVideoTitleDisplay(position, timeout);
}
/**
* Selects an audio output module.
* Any change will take effect only after playback is stopped and
* restarted. Audio output cannot be changed while playing.
*
* @return true on success.
*/
public boolean setAudioOutput(String aout) {
final boolean ret = nativeSetAudioOutput(aout);
if (ret) {
synchronized (this) {
mAudioOutput = aout;
}
}
return ret;
}
/**
* Configures an explicit audio output device.
* Audio output will be moved to the device specified by the device identifier string.
*
* @return true on success.
*/
public boolean setAudioOutputDevice(String id) {
final boolean ret = nativeSetAudioOutputDevice(id);
if (ret) {
synchronized (this) {
mAudioOutputDevice = id;
}
}
return ret;
}
/**
* Get the full description of available titles.
*
* @return the list of titles
*/
public Title[] getTitles() {
return nativeGetTitles();
}
/**
* Get the full description of available chapters.
*
* @param title index of the title (if -1, use the current title)
* @return the list of Chapters for the title
*/
public Chapter[] getChapters(int title) {
return nativeGetChapters(title);
}
/**
* Get the number of available video tracks.
*/
public int getVideoTracksCount() {
return nativeGetVideoTracksCount();
}
/**
* Get the list of available video tracks.
*/
public TrackDescription[] getVideoTracks() {
return nativeGetVideoTracks();
}
/**
* Get the current video track.
*
* @return the video track ID or -1 if no active input
*/
public int getVideoTrack() {
return nativeGetVideoTrack();
}
/**
* Set the video track.
*
* @return true on success.
*/
public boolean setVideoTrack(int index) {
return nativeSetVideoTrack(index);
}
private void setVideoTrackEnabled(boolean enabled) {
if (!enabled) {
setVideoTrack(-1);
} else {
final MediaPlayer.TrackDescription tracks[] = getVideoTracks();
if (tracks != null) {
for (MediaPlayer.TrackDescription track : tracks) {
if (track.id != -1) {
setVideoTrack(track.id);
break;
}
}
}
}
}
/**
* Get the number of available audio tracks.
*/
public int getAudioTracksCount() {
return nativeGetAudioTracksCount();
}
/**
* Get the list of available audio tracks.
*/
public TrackDescription[] getAudioTracks() {
return nativeGetAudioTracks();
}
/**
* Get the current audio track.
*
* @return the audio track ID or -1 if no active input
*/
public int getAudioTrack() {
return nativeGetAudioTrack();
}
/**
* Set the audio track.
*
* @return true on success.
*/
public boolean setAudioTrack(int index) {
return nativeSetAudioTrack(index);
}
/**
* Get the current audio delay.
*
* @return delay in microseconds.
*/
public long getAudioDelay() {
return nativeGetAudioDelay();
}
/**
* Set current audio delay. The audio delay will be reset to zero each time the media changes.
*
* @param delay in microseconds.
* @return true on success.
*/
public boolean setAudioDelay(long delay) {
return nativeSetAudioDelay(delay);
}
/**
* Get the number of available spu (subtitle) tracks.
*/
public int getSpuTracksCount() {
return nativeGetSpuTracksCount();
}
/**
* Get the list of available spu (subtitle) tracks.
*/
public TrackDescription[] getSpuTracks() {
return nativeGetSpuTracks();
}
/**
* Get the current spu (subtitle) track.
*
* @return the spu (subtitle) track ID or -1 if no active input
*/
public int getSpuTrack() {
return nativeGetSpuTrack();
}
/**
* Set the spu (subtitle) track.
*
* @return true on success.
*/
public boolean setSpuTrack(int index) {
return nativeSetSpuTrack(index);
}
/**
* Get the current spu (subtitle) delay.
*
* @return delay in microseconds.
*/
public long getSpuDelay() {
return nativeGetSpuDelay();
}
/**
* Set current spu (subtitle) delay. The spu delay will be reset to zero each time the media changes.
*
* @param delay in microseconds.
* @return true on success.
*/
public boolean setSpuDelay(long delay) {
return nativeSetSpuDelay(delay);
}
/**
* Apply new equalizer settings to a media player.
*
* The equalizer is first created by invoking {@link Equalizer#create()} or
* {@link Equalizer#createFromPreset(int)}}.
*
* It is possible to apply new equalizer settings to a media player whether the media
* player is currently playing media or not.
*
* Invoking this method will immediately apply the new equalizer settings to the audio
* output of the currently playing media if there is any.
*
* If there is no currently playing media, the new equalizer settings will be applied
* later if and when new media is played.
*
* Equalizer settings will automatically be applied to subsequently played media.
*
* To disable the equalizer for a media player invoke this method passing null.
*
* @return true on success.
*/
public boolean setEqualizer(Equalizer equalizer) {
return nativeSetEqualizer(equalizer);
}
/**
* Set a new video subtitle file.
*
* @param path local path.
* @return true on success.
*/
public boolean setSubtitleFile(String path) {
return nativeSetSubtitleFile(path);
}
/**
* Sets the speed of playback (1 being normal speed, 2 being twice as fast)
*
* @param rate
*/
public native void setRate(float rate);
/**
* Get the current playback speed
*/
public native float getRate();
/**
* Returns true if any media is playing
*/
public native boolean isPlaying();
/**
* Returns true if any media is seekable
*/
public native boolean isSeekable();
/**
* Pauses any playing media
*/
public native void pause();
/**
* Get player state.
*/
public native int getPlayerState();
/**
* Gets volume as integer
*/
public native int getVolume();
/**
* Sets volume as integer
* @param volume: Volume level passed as integer
*/
public native int setVolume(int volume);
/**
* Gets the current movie time (in ms).
* @return the movie time (in ms), or -1 if there is no media.
*/
public native long getTime();
/**
* Sets the movie time (in ms), if any media is being played.
* @param time: Time in ms.
* @return the movie time (in ms), or -1 if there is no media.
*/
public native long setTime(long time);
/**
* Gets the movie position.
* @return the movie position, or -1 for any error.
*/
public native float getPosition();
/**
* Sets the movie position.
* @param pos: movie position.
*/
public native void setPosition(float pos);
/**
* Gets current movie's length in ms.
* @return the movie length (in ms), or -1 if there is no media.
*/
public native long getLength();
public native int getTitle();
public native void setTitle(int title);
public native int getChapter();
public native int previousChapter();
public native int nextChapter();
public native void setChapter(int chapter);
public native void navigate(int navigate);
public synchronized void setEventListener(EventListener listener) {
super.setEventListener(listener);
}
@Override
protected synchronized Event onEventNative(int eventType, long arg1, float arg2) {
switch (eventType) {
case Event.Stopped:
case Event.EndReached:
case Event.EncounteredError:
mVoutCount = 0;
notify();
case Event.Opening:
case Event.Playing:
case Event.Paused:
return new Event(eventType);
case Event.TimeChanged:
return new Event(eventType, arg1);
case Event.PositionChanged:
return new Event(eventType, arg2);
case Event.Vout:
mVoutCount = (int) arg1;
notify();
return new Event(eventType, arg1);
case Event.ESAdded:
case Event.ESDeleted:
case Event.SeekableChanged:
case Event.PausableChanged:
return new Event(eventType, arg1);
}
return null;
}
@Override
protected void onReleaseNative() {
if (mMedia != null)
mMedia.release();
nativeRelease();
}
/* JNI */
private native void nativeNewFromLibVlc(LibVLC libVLC, IAWindowNativeHandler window);
private native void nativeNewFromMedia(Media media, IAWindowNativeHandler window);
private native void nativeRelease();
private native void nativeSetMedia(Media media);
private native void nativePlay();
private native void nativeStop();
private native void nativeSetVideoTitleDisplay(int position, int timeout);
private native boolean nativeSetAudioOutput(String aout);
private native boolean nativeSetAudioOutputDevice(String id);
private native Title[] nativeGetTitles();
private native Chapter[] nativeGetChapters(int title);
private native int nativeGetVideoTracksCount();
private native TrackDescription[] nativeGetVideoTracks();
private native int nativeGetVideoTrack();
private native boolean nativeSetVideoTrack(int index);
private native int nativeGetAudioTracksCount();
private native TrackDescription[] nativeGetAudioTracks();
private native int nativeGetAudioTrack();
private native boolean nativeSetAudioTrack(int index);
private native long nativeGetAudioDelay();
private native boolean nativeSetAudioDelay(long delay);
private native int nativeGetSpuTracksCount();
private native TrackDescription[] nativeGetSpuTracks();
private native int nativeGetSpuTrack();
private native boolean nativeSetSpuTrack(int index);
private native long nativeGetSpuDelay();
private native boolean nativeSetSpuDelay(long delay);
private native boolean nativeSetSubtitleFile(String path);
private native boolean nativeSetEqualizer(Equalizer equalizer);
}