/*****************************************************************************
* LibVLC.java
*****************************************************************************
* Copyright © 2010-2013 VLC authors and VideoLAN
*
* 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;
import java.util.ArrayList;
import java.util.Map;
import android.content.Context;
import android.os.Build;
import android.util.Log;
import android.view.Surface;
public class LibVLC {
private static final String TAG = "VLC/LibVLC";
public static final int AOUT_AUDIOTRACK_JAVA = 0;
public static final int AOUT_AUDIOTRACK = 1;
public static final int AOUT_OPENSLES = 2;
public static final int VOUT_ANDROID_SURFACE = 0;
public static final int VOUT_OPEGLES2 = 1;
private static LibVLC sInstance;
/** libVLC instance C pointer */
private long mLibVlcInstance = 0; // Read-only, reserved for JNI
/** libvlc_media_player pointer and index */
private int mInternalMediaPlayerIndex = 0; // Read-only, reserved for JNI
private long mInternalMediaPlayerInstance = 0; // Read-only, reserved for JNI
private MediaList mMediaList; // Pointer to media list being followed
private MediaList mPrimaryList; // Primary/default media list; see getPrimaryMediaList()
/** Buffer for VLC messages */
private StringBuffer mDebugLogBuffer;
private boolean mIsBufferingLog = false;
private Aout mAout;
/** Keep screen bright */
//private WakeLock mWakeLock;
/** Settings */
private int hardwareAcceleration = -1;
private String subtitlesEncoding = "";
private int aout = LibVlcUtil.isGingerbreadOrLater() ? AOUT_OPENSLES : AOUT_AUDIOTRACK_JAVA;
private int vout = VOUT_ANDROID_SURFACE;
private boolean timeStretching = false;
private int deblocking = -1;
private String chroma = "";
private boolean verboseMode = true;
private float[] equalizer = null;
private boolean frameSkip = false;
private int networkCaching = 0;
/** Check in libVLC already initialized otherwise crash */
private boolean mIsInitialized = false;
public native void attachSurface(Surface surface, IVideoPlayer player);
public native void detachSurface();
public native void attachSubtitlesSurface(Surface surface);
public native void detachSubtitlesSurface();
/* Load library before object instantiation */
static {
try {
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1)
System.loadLibrary("iomx-gingerbread");
else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB_MR2)
System.loadLibrary("iomx-hc");
else if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2)
System.loadLibrary("iomx-ics");
} catch (Throwable t) {
Log.w(TAG, "Unable to load the iomx library: " + t);
}
try {
System.loadLibrary("vlcjni");
} catch (UnsatisfiedLinkError ule) {
Log.e(TAG, "Can't load vlcjni library: " + ule);
/// FIXME Alert user
System.exit(1);
} catch (SecurityException se) {
Log.e(TAG, "Encountered a security issue when loading vlcjni library: " + se);
/// FIXME Alert user
System.exit(1);
}
}
/**
* Singleton constructor of libVLC Without surface and vout to create the
* thumbnail and get information e.g. on the MediaLibraryActivity
*
* @return libVLC instance
* @throws LibVlcException
*/
public static LibVLC getInstance() throws LibVlcException {
synchronized (LibVLC.class) {
if (sInstance == null) {
/* First call */
sInstance = new LibVLC();
}
}
return sInstance;
}
/**
* Return an existing instance of libVLC Call it when it is NOT important
* that this fails
*
* @return libVLC instance OR null
*/
public static LibVLC getExistingInstance() {
synchronized (LibVLC.class) {
return sInstance;
}
}
/**
* Constructor
* It is private because this class is a singleton.
*/
private LibVLC() {
mAout = new Aout();
}
/**
* Destructor:
* It is bad practice to rely on them, so please don't forget to call
* destroy() before exiting.
*/
@Override
public void finalize() {
if (mLibVlcInstance != 0) {
Log.d(TAG, "LibVLC is was destroyed yet before finalize()");
destroy();
}
}
/**
* Get the media list that LibVLC is following right now.
*
* @return The media list object being followed
*/
public MediaList getMediaList() {
return mMediaList;
}
/**
* Set the media list for LibVLC to follow.
*
* @param mediaList The media list object to follow
*/
public void setMediaList(MediaList mediaList) {
mMediaList = mediaList;
}
/**
* Sets LibVLC to follow the default media list (see below)
*/
public void setMediaList() {
mMediaList = mPrimaryList;
}
/**
* Gets the primary media list, or the "currently playing" list.
* Not to be confused with the media list pointer from above, which
* refers the the MediaList object that libVLC is currently following.
* This list is just one out of many lists that it can be pointed towards.
*
* This list will be used for lists of songs that are not user-defined.
* For example: selecting a song from the Songs list, or from the list
* displayed after selecting an album.
*
* It is loaded as the default list.
*
* @return The primary media list
*/
public MediaList getPrimaryMediaList() {
return mPrimaryList;
}
/**
* Give to LibVLC the surface to draw the video.
* @param f the surface to draw
*/
public native void setSurface(Surface f);
public static synchronized void restart(Context context) {
if (sInstance != null) {
try {
sInstance.destroy();
sInstance.init(context);
} catch (LibVlcException lve) {
Log.e(TAG, "Unable to reinit libvlc: " + lve);
}
}
}
/**
* those get/is* are called from native code to get settings values.
*/
public int getHardwareAcceleration() {
return this.hardwareAcceleration;
}
public void setHardwareAcceleration(int hardwareAcceleration) {
if (hardwareAcceleration < 0) {
// Automatic mode: activate MediaCodec opaque direct rendering for 4.3 and above.
if (LibVlcUtil.isJellyBeanMR2OrLater())
this.hardwareAcceleration = 2;
else
this.hardwareAcceleration = 0;
}
else
this.hardwareAcceleration = hardwareAcceleration;
}
public String getSubtitlesEncoding() {
return subtitlesEncoding;
}
public void setSubtitlesEncoding(String subtitlesEncoding) {
this.subtitlesEncoding = subtitlesEncoding;
}
public int getAout() {
return aout;
}
public void setAout(int aout) {
if (aout < 0)
this.aout = LibVlcUtil.isGingerbreadOrLater() ? AOUT_OPENSLES : AOUT_AUDIOTRACK_JAVA;
else
this.aout = aout;
}
public int getVout() {
return vout;
}
public void setVout(int vout) {
if (vout < 0)
this.vout = VOUT_ANDROID_SURFACE;
else
this.vout = vout;
}
public boolean timeStretchingEnabled() {
return timeStretching;
}
public void setTimeStretching(boolean timeStretching) {
this.timeStretching = timeStretching;
}
public int getDeblocking() {
int ret = deblocking;
if(deblocking < 0) {
/**
* Set some reasonable deblocking defaults:
*
* Skip all (4) for armv6 and MIPS by default
* Skip non-ref (1) for all armv7 more than 1.2 Ghz and more than 2 cores
* Skip non-key (3) for all devices that don't meet anything above
*/
LibVlcUtil.MachineSpecs m = LibVlcUtil.getMachineSpecs();
if( (m.hasArmV6 && !(m.hasArmV7)) || m.hasMips )
ret = 4;
else if(m.bogoMIPS > 1200 && m.processors > 2)
ret = 1;
else
ret = 3;
} else if(deblocking > 4) { // sanity check
ret = 3;
}
return ret;
}
public void setDeblocking(int deblocking) {
this.deblocking = deblocking;
}
public String getChroma() {
return chroma;
}
public void setChroma(String chroma) {
this.chroma = chroma.equals("YV12") && !LibVlcUtil.isGingerbreadOrLater() ? "" : chroma;
}
public boolean isVerboseMode() {
return verboseMode;
}
public void setVerboseMode(boolean verboseMode) {
this.verboseMode = verboseMode;
}
public float[] getEqualizer()
{
return equalizer;
}
public void setEqualizer(float[] equalizer)
{
this.equalizer = equalizer;
applyEqualizer();
}
private void applyEqualizer()
{
setNativeEqualizer(mInternalMediaPlayerInstance, this.equalizer);
}
private native int setNativeEqualizer(long mediaPlayer, float[] bands);
public boolean frameSkipEnabled() {
return frameSkip;
}
public void setFrameSkip(boolean frameskip) {
this.frameSkip = frameskip;
}
public int getNetworkCaching() {
return this.networkCaching;
}
public void setNetworkCaching(int networkcaching) {
this.networkCaching = networkcaching;
}
/**
* Initialize the libVLC class.
*
* This function must be called before using any libVLC functions.
*
* @throws LibVlcException
*/
public void init(Context context) throws LibVlcException {
Log.v(TAG, "Initializing LibVLC");
mDebugLogBuffer = new StringBuffer();
if (!mIsInitialized) {
if(!LibVlcUtil.hasCompatibleCPU(context)) {
Log.e(TAG, LibVlcUtil.getErrorMsg());
throw new LibVlcException();
}
nativeInit();
mMediaList = mPrimaryList = new MediaList(this);
setEventHandler(EventHandler.getInstance());
mIsInitialized = true;
}
}
/**
* Destroy this libVLC instance
* @note You must call it before exiting
*/
public void destroy() {
Log.v(TAG, "Destroying LibVLC instance");
nativeDestroy();
detachEventHandler();
mIsInitialized = false;
}
/**
* Open the Java audio output.
* This function is called by the native code
*/
public void initAout(int sampleRateInHz, int channels, int samples) {
Log.d(TAG, "Opening the java audio output");
mAout.init(sampleRateInHz, channels, samples);
}
/**
* Play an audio buffer taken from the native code
* This function is called by the native code
*/
public void playAudio(byte[] audioData, int bufferSize) {
mAout.playBuffer(audioData, bufferSize);
}
/**
* Pause the Java audio output
* This function is called by the native code
*/
public void pauseAout() {
Log.d(TAG, "Pausing the java audio output");
mAout.pause();
}
/**
* Close the Java audio output
* This function is called by the native code
*/
public void closeAout() {
Log.d(TAG, "Closing the java audio output");
mAout.release();
}
/**
* Play a media from the media list (playlist)
*
* @param position The index of the media
*/
public void playIndex(int position) {
String mrl = mMediaList.getMRL(position);
if (mrl == null)
return;
String[] options = mMediaList.getMediaOptions(position);
mInternalMediaPlayerIndex = position;
playMRL(mLibVlcInstance, mrl, options);
}
/**
* Play an MRL directly.
*
* @param mrl MRL of the media to play.
*/
public void playMRL(String mrl) {
// index=-1 will return options from libvlc instance without relying on MediaList
String[] options = mMediaList.getMediaOptions(-1);
mInternalMediaPlayerIndex = 0;
playMRL(mLibVlcInstance, mrl, options);
}
public TrackInfo[] readTracksInfo(String mrl) {
return readTracksInfo(mLibVlcInstance, mrl);
}
/**
* Get a media thumbnail.
*/
public byte[] getThumbnail(String mrl, int i_width, int i_height) {
return getThumbnail(mLibVlcInstance, mrl, i_width, i_height);
}
/**
* Return true if there is a video track in the file
*/
public boolean hasVideoTrack(String mrl) throws java.io.IOException {
return hasVideoTrack(mLibVlcInstance, mrl);
}
/**
* 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();
/**
* Initialize the libvlc C library
* @return a pointer to the libvlc instance
*/
private native void nativeInit() throws LibVlcException;
/**
* Close the libvlc C library
* @note mLibVlcInstance should be 0 after a call to destroy()
*/
private native void nativeDestroy();
/**
* Start buffering to the mDebugLogBuffer.
*/
public native void startDebugBuffer();
public native void stopDebugBuffer();
public String getBufferContent() {
return mDebugLogBuffer.toString();
}
public void clearBuffer() {
mDebugLogBuffer.setLength(0);
}
public boolean isDebugBuffering() {
return mIsBufferingLog;
}
/**
* Play an mrl
*/
private native void playMRL(long instance, String mrl, String[] mediaOptions);
/**
* Returns true if any media is playing
*/
public native boolean isPlaying();
/**
* Returns true if any media is seekable
*/
public native boolean isSeekable();
/**
* Plays any loaded media
*/
public native void play();
/**
* Pauses any playing media
*/
public native void pause();
/**
* Stops any playing media
*/
public native void stop();
/**
* 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();
/**
* Get the libVLC version
* @return the libVLC version string
*/
public native String version();
/**
* Get the libVLC compiler
* @return the libVLC compiler string
*/
public native String compiler();
/**
* Get the libVLC changeset
* @return the libVLC changeset string
*/
public native String changeset();
/**
* Get a media thumbnail.
* @return a bytearray with the RGBA thumbnail data inside.
*/
private native byte[] getThumbnail(long instance, String mrl, int i_width, int i_height);
/**
* Return true if there is a video track in the file
*/
private native boolean hasVideoTrack(long instance, String mrl);
private native TrackInfo[] readTracksInfo(long instance, String mrl);
public native TrackInfo[] readTracksInfoInternal();
public native int getAudioTracksCount();
public native Map<Integer,String> getAudioTrackDescription();
public native int getAudioTrack();
public native int setAudioTrack(int index);
public native int getVideoTracksCount();
public native int addSubtitleTrack(String path);
public native Map<Integer,String> getSpuTrackDescription();
public native int getSpuTrack();
public native int setSpuTrack(int index);
public native int getSpuTracksCount();
public static native String nativeToURI(String path);
/**
* Quickly converts path to URIs, which are mandatory in libVLC.
*
* @param path
* The path to be converted.
* @return A URI representation of path
*/
public static String PathToURI(String path) {
if(path == null) {
throw new NullPointerException("Cannot convert null path!");
}
return LibVLC.nativeToURI(path);
}
public static native void nativeReadDirectory(String path, ArrayList<String> res);
public native static boolean nativeIsPathDirectory(String path);
/**
* Expand and continue playing the current media.
*
* @return the index of the media was expanded, and -1 if no media was expanded
*/
public int expandAndPlay() {
int r = mMediaList.expandMedia(mInternalMediaPlayerIndex);
if(r == 0)
this.playIndex(mInternalMediaPlayerIndex);
return r;
}
/**
* Expand the current media.
* @return the index of the media was expanded, and -1 if no media was expanded
*/
public int expand() {
return mMediaList.expandMedia(mInternalMediaPlayerIndex);
}
private native void setEventHandler(EventHandler eventHandler);
private native void detachEventHandler();
public native float[] getBands();
public native String[] getPresets();
public native float[] getPreset(int index);
}