/*****************************************************************************
* LibVLC.java
*****************************************************************************
* Copyright © 2010-2014 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.io.File;
import java.util.ArrayList;
import java.util.Map;
import android.content.Context;
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;
public static final int VOUT_ANDROID_WINDOW = 2;
public static final int HW_ACCELERATION_AUTOMATIC = -1;
public static final int HW_ACCELERATION_DISABLED = 0;
public static final int HW_ACCELERATION_DECODING = 1;
public static final int HW_ACCELERATION_FULL = 2;
public static final int DEV_HW_DECODER_AUTOMATIC = -1;
public static final int DEV_HW_DECODER_OMX = 0;
public static final int DEV_HW_DECODER_OMX_DR = 1;
public static final int DEV_HW_DECODER_MEDIACODEC = 2;
public static final int DEV_HW_DECODER_MEDIACODEC_DR = 3;
public static final int INPUT_NAV_ACTIVATE = 0;
public static final int INPUT_NAV_UP = 1;
public static final int INPUT_NAV_DOWN = 2;
public static final int INPUT_NAV_LEFT = 3;
public static final int INPUT_NAV_RIGHT = 4;
private static final String DEFAULT_CODEC_LIST = "mediacodec,iomx,all";
private static final boolean HAS_WINDOW_VOUT = LibVlcUtil.isGingerbreadOrLater();
private static LibVLC sInstance;
private final EventHandler eventHandler = EventHandler.getInstance();
/** 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 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 AudioOutput mAout;
/** Keep screen bright */
//private WakeLock mWakeLock;
/** Settings */
private int hardwareAcceleration = HW_ACCELERATION_AUTOMATIC;
private int devHardwareDecoder = DEV_HW_DECODER_AUTOMATIC;
private String codecList = DEFAULT_CODEC_LIST;
private String devCodecList = null;
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;
private boolean httpReconnect = false;
/** Path of application-specific cache */
private String mCachePath = "";
/** 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();
public native void eventVideoPlayerActivityCreated(boolean created);
/**
* 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.
*/
public LibVLC() {
mAout = new AudioOutput();
sInstance = this;
}
/**
* Destructor:
* It is bad practice to rely on them, so please don't forget to call
* destroy() before exiting.
*/
@Override
protected 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 restartInstance(Context context) {
if (sInstance != null) {
try {
sInstance.destroy();
sInstance.init(context);
} catch (LibVlcException lve) {
Log.e(TAG, "Unable to reinit libvlc: " + lve);
}
}
}
public void restart(Context context) {
try {
this.destroy();
this.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 == HW_ACCELERATION_DISABLED) {
Log.d(TAG, "HWDec disabled: by user");
this.hardwareAcceleration = HW_ACCELERATION_DISABLED;
this.codecList = "all";
} else {
// Automatic or forced
HWDecoderUtil.Decoder decoder = HWDecoderUtil.getDecoderFromDevice();
if (decoder == HWDecoderUtil.Decoder.NONE) {
// NONE
this.hardwareAcceleration = HW_ACCELERATION_DISABLED;
this.codecList = "all";
Log.d(TAG, "HWDec disabled: device not working with mediacodec,iomx");
} else if (decoder == HWDecoderUtil.Decoder.UNKNOWN) {
// UNKNOWN
if (hardwareAcceleration < 0) {
this.hardwareAcceleration = HW_ACCELERATION_DISABLED;
this.codecList = "all";
Log.d(TAG, "HWDec disabled: automatic and (unknown device or android version < 4.3)");
} else {
this.hardwareAcceleration = hardwareAcceleration;
this.codecList = DEFAULT_CODEC_LIST;
Log.d(TAG, "HWDec enabled: forced by user and unknown device");
}
} else {
// OMX, MEDIACODEC or ALL
this.hardwareAcceleration = hardwareAcceleration < 0 ?
HW_ACCELERATION_FULL : hardwareAcceleration;
if (decoder == HWDecoderUtil.Decoder.ALL)
this.codecList = DEFAULT_CODEC_LIST;
else {
final StringBuilder sb = new StringBuilder();
if (decoder == HWDecoderUtil.Decoder.MEDIACODEC)
sb.append("mediacodec,");
else if (decoder == HWDecoderUtil.Decoder.OMX)
sb.append("iomx,");
sb.append("all");
this.codecList = sb.toString();
}
Log.d(TAG, "HWDec enabled: device working with: " + this.codecList);
}
}
}
public int getDevHardwareDecoder() {
return this.devHardwareDecoder;
}
public void setDevHardwareDecoder(int devHardwareDecoder) {
if (devHardwareDecoder != DEV_HW_DECODER_AUTOMATIC) {
this.devHardwareDecoder = devHardwareDecoder;
if (this.devHardwareDecoder == DEV_HW_DECODER_OMX ||
this.devHardwareDecoder == DEV_HW_DECODER_OMX_DR)
this.devCodecList = "iomx";
else
this.devCodecList = "mediacodec";
Log.d(TAG, "HWDec forced: " + this.devCodecList +
(isDirectRendering() ? "-dr" : ""));
this.devCodecList += ",none";
} else {
this.devHardwareDecoder = DEV_HW_DECODER_AUTOMATIC;
this.devCodecList = null;
}
}
public boolean isDirectRendering() {
if (!HAS_WINDOW_VOUT)
return false;
if (devHardwareDecoder != DEV_HW_DECODER_AUTOMATIC) {
return (this.devHardwareDecoder == DEV_HW_DECODER_OMX_DR ||
this.devHardwareDecoder == DEV_HW_DECODER_MEDIACODEC_DR);
} else {
return this.hardwareAcceleration == HW_ACCELERATION_FULL;
}
}
public String[] getMediaOptions(boolean noHardwareAcceleration, boolean noVideo, boolean useTcp) {
if (this.devHardwareDecoder != DEV_HW_DECODER_AUTOMATIC)
noHardwareAcceleration = noVideo = false;
else if (!noHardwareAcceleration)
noHardwareAcceleration = getHardwareAcceleration() == HW_ACCELERATION_DISABLED;
ArrayList<String> options = new ArrayList<String>();
if (!noHardwareAcceleration) {
/*
* Set higher caching values if using iomx decoding, since some omx
* decoders have a very high latency, and if the preroll data isn't
* enough to make the decoder output a frame, the playback timing gets
* started too soon, and every decoded frame appears to be too late.
* On Nexus One, the decoder latency seems to be 25 input packets
* for 320x170 H.264, a few packets less on higher resolutions.
* On Nexus S, the decoder latency seems to be about 7 packets.
*/
options.add(":file-caching=1500");
options.add(":network-caching=1500");
options.add(":codec="+ (this.devCodecList != null ? this.devCodecList : this.codecList));
}
if (noVideo)
options.add(":no-video");
if (useTcp)
options.add(":rtsp-tcp");
return options.toArray(new String[options.size()]);
}
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.isICSOrLater() ? 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;
if (this.vout == VOUT_ANDROID_SURFACE && HAS_WINDOW_VOUT)
this.vout = VOUT_ANDROID_WINDOW;
}
public boolean useCompatSurface() {
return this.vout != VOUT_ANDROID_WINDOW;
}
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 == null)
return ret;
if( (m.hasArmV6 && !(m.hasArmV7)) || m.hasMips )
ret = 4;
else if(m.frequency >= 1200 && m.processors > 2)
ret = 1;
else if(m.bogoMIPS >= 1200 && m.processors > 2) {
ret = 1;
Log.d(TAG, "Used bogoMIPS due to lack of frequency info");
} 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();
}
protected void applyEqualizer()
{
setNativeEqualizer(this.equalizer);
}
private native int setNativeEqualizer(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;
}
public boolean getHttpReconnect() {
return httpReconnect;
}
public void setHttpReconnect(boolean httpReconnect) {
this.httpReconnect = httpReconnect;
}
/**
* 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 (LibVlcUtil.isLibraryLoaded() && !mIsInitialized) {
if(!LibVlcUtil.hasCompatibleCPU(context)) {
Log.e(TAG, LibVlcUtil.getErrorMsg());
throw new LibVlcException();
}
File cacheDir = context.getCacheDir();
mCachePath = (cacheDir != null) ? cacheDir.getAbsolutePath() : null;
nativeInit();
mMediaList = mPrimaryList = new MediaList(this);
mIsInitialized = true;
}
}
/**
* Destroy this libVLC instance
* @note You must call it before exiting
*/
public void destroy() {
Log.v(TAG, "Destroying LibVLC instance");
nativeDestroy();
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(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(mrl, options);
}
/**
* 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();
/**
* Do pan and digital zoom.
*
* @param crop_top amount to crop top
* @param crop_bottom amount to crop bottom
* @param crop_left amount to crop left
* @param crop_right amount to crop right
*/
public native void panDigitalZoom(int crop_top, int crop_bottom, int crop_left, int crop_right);
public native int getWidth();
public native int getHeight();
/**
* 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(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();
/**
* 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();
/**
* 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.
*/
public native byte[] getThumbnail(String mrl, int i_width, int i_height);
/**
* Return true if there is a video track in the file
*/
public native boolean hasVideoTrack(String mrl) throws java.io.IOException;
public native TrackInfo[] readTracksInfo(String mrl);
public native TrackInfo[] readTracksInfoInternal();
public native int getAudioTracksCount();
public native Map<Integer,String> getAudioTrackDescription();
public native Map<String, Object> getStats();
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);
public native static void sendMouseEvent( int action, int button, int x, int y);
/**
* 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);
}
public native float[] getBands();
public native String[] getPresets();
public native float[] getPreset(int index);
public EventHandler getEventHandler() {
return eventHandler;
}
public String getCachePath() {
return mCachePath;
}
public native int getTitle();
public native void setTitle(int title);
public native int getChapterCountForTitle(int title);
public native int getTitleCount();
public native void playerNavigate(int navigate);
public native String getMeta(int meta);
public native int setWindowSize(int width, int height);
/* MediaList */
protected native void loadPlaylist(String mrl, ArrayList<String> items);
protected native int expandMedia(int position, ArrayList<String> children);
}