Merge "MediaPlayer2: clean up more code"
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 0b3c973..4e90162 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -294,7 +294,7 @@
* reached end of stream and been paused, or never started before,
* playback will start at the beginning.
*
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object play();
@@ -305,21 +305,21 @@
* After setting the datasource and the display surface, you need to
* call prepare().
*
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object prepare();
/**
* Pauses playback. Call play() to resume.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object pause();
/**
* Tries to play next data source if applicable.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object skipToNext();
@@ -404,7 +404,7 @@
* You must call this method before {@link #play()} and {@link #pause()} in order
* for the audio attributes to become effective thereafter.
* @param attributes a non-null set of audio attributes
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setAudioAttributes(@NonNull AudioAttributes attributes);
@@ -419,7 +419,7 @@
* Sets the data source as described by a DataSourceDesc.
*
* @param dsd the descriptor of data source you want to play
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setDataSource(@NonNull DataSourceDesc dsd);
@@ -429,7 +429,7 @@
* after current data source is finished.
*
* @param dsd the descriptor of data source you want to play after current one
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setNextDataSource(@NonNull DataSourceDesc dsd);
@@ -438,14 +438,14 @@
* Sets a list of data sources to be played sequentially after current data source is done.
*
* @param dsds the list of data sources you want to play after current one
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setNextDataSources(@NonNull List<DataSourceDesc> dsds);
/**
* Removes all data sources pending to be played.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object clearNextDataSources();
@@ -460,7 +460,7 @@
/**
* Configures the player to loop on the current data source.
* @param loop true if the current data source is meant to loop.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object loopCurrent(boolean loop);
@@ -473,7 +473,7 @@
* A value of 0.0f indicates muting, a value of 1.0f is the nominal unattenuated and unamplified
* gain. See {@link #getMaxPlayerVolume()} for the volume range supported by this player.
* @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setPlayerVolume(float volume);
@@ -502,7 +502,7 @@
*
* @param label An application specific Object used to help to identify the completeness
* of a batch of commands.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object notifyWhenCommandLabelReached(@NonNull Object label);
@@ -518,7 +518,7 @@
* played.
*
* @param sh the SurfaceHolder to use for video display
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
public abstract Object setDisplay(SurfaceHolder sh);
@@ -538,7 +538,7 @@
*
* @param surface The {@link Surface} to be used for the video portion of
* the media.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setSurface(Surface surface);
@@ -558,7 +558,7 @@
*
* @param context the Context to use
* @param mode the power/wake mode to set
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
* @see android.os.PowerManager
*/
// This is an asynchronous call.
@@ -572,7 +572,7 @@
* access.
*
* @param screenOn Supply true to keep the screen on, false to allow it to turn off.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setScreenOnWhilePlaying(boolean screenOn);
@@ -667,22 +667,11 @@
* available for the media being handled by this instance of MediaPlayer2
* The attributes are descibed in {@link MetricsConstants}.
*
- * Additional vendor-specific fields may also be present in
- * the return value.
+ * Additional vendor-specific fields may also be present in the return value.
*/
public abstract PersistableBundle getMetrics();
/**
- * Checks whether the MediaPlayer2 is playing.
- *
- * @return true if currently playing, false otherwise
- * @throws IllegalStateException if the internal player engine has not been
- * initialized or has been released.
- * @hide
- */
- public abstract boolean isPlaying();
-
- /**
* Gets the current buffering management params used by the source component.
* Calling it only after {@code setDataSource} has been called.
* Each type of data source might have different set of default params.
@@ -690,10 +679,10 @@
* @return the current buffering management params used by the source component.
* @throws IllegalStateException if the internal player engine has not been
* initialized, or {@code setDataSource} has not been called.
- * @hide
*/
+ // TODO: make it public when ready
@NonNull
- public BufferingParams getBufferingParams() {
+ BufferingParams getBufferingParams() {
return new BufferingParams.Builder().build();
}
@@ -705,80 +694,11 @@
* The input is a hint to MediaPlayer2.
*
* @param params the buffering management params.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
- *
- * @hide
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
+ // TODO: make it public when ready
// This is an asynchronous call.
- public abstract Object setBufferingParams(@NonNull BufferingParams params);
-
- /**
- * Change playback speed of audio by resampling the audio.
- * <p>
- * Specifies resampling as audio mode for variable rate playback, i.e.,
- * resample the waveform based on the requested playback rate to get
- * a new waveform, and play back the new waveform at the original sampling
- * frequency.
- * When rate is larger than 1.0, pitch becomes higher.
- * When rate is smaller than 1.0, pitch becomes lower.
- *
- * @hide
- */
- public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2;
-
- /**
- * Change playback speed of audio without changing its pitch.
- * <p>
- * Specifies time stretching as audio mode for variable rate playback.
- * Time stretching changes the duration of the audio samples without
- * affecting its pitch.
- * <p>
- * This mode is only supported for a limited range of playback speed factors,
- * e.g. between 1/2x and 2x.
- *
- * @hide
- */
- public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1;
-
- /**
- * Change playback speed of audio without changing its pitch, and
- * possibly mute audio if time stretching is not supported for the playback
- * speed.
- * <p>
- * Try to keep audio pitch when changing the playback rate, but allow the
- * system to determine how to change audio playback if the rate is out
- * of range.
- *
- * @hide
- */
- public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0;
-
- /** @hide */
- @IntDef(flag = false, prefix = "PLAYBACK_RATE_AUDIO_MODE", value = {
- PLAYBACK_RATE_AUDIO_MODE_DEFAULT,
- PLAYBACK_RATE_AUDIO_MODE_STRETCH,
- PLAYBACK_RATE_AUDIO_MODE_RESAMPLE,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface PlaybackRateAudioMode {}
-
- /**
- * Sets playback rate and audio mode.
- *
- * @param rate the ratio between desired playback rate and normal one.
- * @param audioMode audio playback mode. Must be one of the supported
- * audio modes.
- *
- * @throws IllegalStateException if the internal player engine has not been
- * initialized.
- * @throws IllegalArgumentException if audioMode is not supported.
- *
- * @hide
- */
- @NonNull
- public PlaybackParams easyPlaybackParams(float rate, @PlaybackRateAudioMode int audioMode) {
- return new PlaybackParams();
- }
+ abstract Object setBufferingParams(@NonNull BufferingParams params);
/**
* Sets playback rate using {@link PlaybackParams}. The object sets its internal
@@ -787,7 +707,7 @@
* the object state.
*
* @param params the playback params.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setPlaybackParams(@NonNull PlaybackParams params);
@@ -796,6 +716,7 @@
* Gets the playback params, containing the current playback rate.
*
* @return the playback params.
+ * @throws IllegalStateException if the internal player engine has not been initialized.
*/
@NonNull
public abstract PlaybackParams getPlaybackParams();
@@ -804,7 +725,7 @@
* Sets A/V sync mode.
*
* @param params the A/V sync params to apply
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setSyncParams(@NonNull SyncParams params);
@@ -813,6 +734,7 @@
* Gets the A/V sync mode.
*
* @return the A/V sync params
+ * @throws IllegalStateException if the internal player engine has not been initialized.
*/
@NonNull
public abstract SyncParams getSyncParams();
@@ -822,7 +744,7 @@
* Same as {@link #seekTo(long, int)} with {@code mode = SEEK_PREVIOUS_SYNC}.
*
* @param msec the offset in milliseconds from the start to seek to
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public Object seekTo(long msec) {
@@ -882,7 +804,8 @@
/**
* Moves the media to specified time position by considering the given mode.
* <p>
- * When seekTo is finished, the user will be notified via OnSeekComplete supplied by the user.
+ * When seekTo is finished, the user will be notified via
+ * {@link EventCallback#onCallCompleted} with {@link #CALL_COMPLETED_SEEK_TO}.
* There is at most one active seekTo processed at any time. If there is a to-be-completed
* seekTo, new seekTo requests will be queued in such a way that only the last request
* is kept. When current seekTo is completed, the queued request will be processed if
@@ -895,7 +818,7 @@
* If msec is negative, time position zero will be used.
* If msec is larger than duration, duration will be used.
* @param mode the mode indicating where exactly to seek to.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object seekTo(long msec, @SeekMode int mode);
@@ -953,7 +876,7 @@
* However, it is possible to force this player to be part of an already existing audio session
* by calling this method.
* This method must be called before one of the overloaded <code> setDataSource </code> methods.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setAudioSessionId(int sessionId);
@@ -979,7 +902,7 @@
* <p>This method must be called after one of the overloaded <code> setDataSource </code>
* methods.
* @param effectId system wide unique id of the effect to attach
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object attachAuxEffect(int effectId);
@@ -996,7 +919,7 @@
* x == 0 -> level = 0
* 0 < x <= R -> level = 10^(72*(x-R)/20/R)
* @param level send level scalar
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object setAuxEffectSendLevel(float level);
@@ -1122,7 +1045,7 @@
* @param index the index of the track to be selected. The valid range of the index
* is 0..total number of track - 1. The total number of tracks as well as the type of
* each individual track can be found by calling {@link #getTrackInfo()} method.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*
* @see MediaPlayer2#getTrackInfo
*/
@@ -1139,7 +1062,7 @@
* @param index the index of the track to be deselected. The valid range of the index
* is 0..total number of tracks - 1. The total number of tracks as well as the type of
* each individual track can be found by calling {@link #getTrackInfo()} method.
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*
* @see MediaPlayer2#getTrackInfo
*/
@@ -1872,7 +1795,7 @@
* from the source through {@code getDrmInfo} or registering a
* {@link DrmEventCallback#onDrmInfo}.
*
- * @return a token which can be used to cancel the operation later with {@link #cancel}.
+ * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
*/
// This is an asynchronous call.
public abstract Object prepareDrm(@NonNull UUID uuid);
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 8f475a1..0431820 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -131,6 +131,13 @@
@GuardedBy("mTaskLock")
private Task mCurrentTask;
+ @GuardedBy("mTaskLock")
+ boolean mIsPreviousCommandSeekTo = false;
+ // |mPreviousSeekPos| and |mPreviousSeekMode| are valid only when |mIsPreviousCommandSeekTo|
+ // is true, and they are accessed on |mHandlerThread| only.
+ long mPreviousSeekPos = -1;
+ int mPreviousSeekMode = SEEK_PREVIOUS_SYNC;
+
@GuardedBy("this")
private boolean mReleased;
@@ -927,14 +934,11 @@
private native PersistableBundle native_getMetrics();
@Override
- public native boolean isPlaying();
-
- @Override
@NonNull
- public native BufferingParams getBufferingParams();
+ native BufferingParams getBufferingParams();
@Override
- public Object setBufferingParams(@NonNull BufferingParams params) {
+ Object setBufferingParams(@NonNull BufferingParams params) {
return addTask(new Task(CALL_COMPLETED_SET_BUFFERING_PARAMS, false) {
@Override
void process() {
@@ -946,42 +950,6 @@
private native void _setBufferingParams(@NonNull BufferingParams params);
- /**
- * Sets playback rate and audio mode.
- *
- * @param rate the ratio between desired playback rate and normal one.
- * @param audioMode audio playback mode. Must be one of the supported
- * audio modes.
- *
- * @throws IllegalStateException if the internal player engine has not been
- * initialized.
- * @throws IllegalArgumentException if audioMode is not supported.
- *
- * @hide
- */
- @Override
- @NonNull
- public PlaybackParams easyPlaybackParams(float rate, @PlaybackRateAudioMode int audioMode) {
- PlaybackParams params = new PlaybackParams();
- params.allowDefaults();
- switch (audioMode) {
- case PLAYBACK_RATE_AUDIO_MODE_DEFAULT:
- params.setSpeed(rate).setPitch(1.0f);
- break;
- case PLAYBACK_RATE_AUDIO_MODE_STRETCH:
- params.setSpeed(rate).setPitch(1.0f)
- .setAudioFallbackMode(params.AUDIO_FALLBACK_MODE_FAIL);
- break;
- case PLAYBACK_RATE_AUDIO_MODE_RESAMPLE:
- params.setSpeed(rate).setPitch(rate);
- break;
- default:
- final String msg = "Audio playback mode " + audioMode + " is not supported";
- throw new IllegalArgumentException(msg);
- }
- return params;
- }
-
@Override
public Object setPlaybackParams(@NonNull PlaybackParams params) {
return addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_PARAMS, false) {
@@ -995,26 +963,10 @@
private native void _setPlaybackParams(@NonNull PlaybackParams params);
- /**
- * Gets the playback params, containing the current playback rate.
- *
- * @return the playback params.
- * @throws IllegalStateException if the internal player engine has not been
- * initialized.
- */
@Override
@NonNull
public native PlaybackParams getPlaybackParams();
- /**
- * Sets A/V sync mode.
- *
- * @param params the A/V sync params to apply
- *
- * @throws IllegalStateException if the internal player engine has not been
- * initialized.
- * @throws IllegalArgumentException if params are not supported.
- */
@Override
public Object setSyncParams(@NonNull SyncParams params) {
return addTask(new Task(CALL_COMPLETED_SET_SYNC_PARAMS, false) {
@@ -1028,48 +980,10 @@
private native void _setSyncParams(@NonNull SyncParams params);
- /**
- * Gets the A/V sync mode.
- *
- * @return the A/V sync params
- *
- * @throws IllegalStateException if the internal player engine has not been
- * initialized.
- */
@Override
@NonNull
public native SyncParams getSyncParams();
- /**
- * Moves the media to specified time position by considering the given mode.
- * <p>
- * When seekTo is finished, the user will be notified via OnSeekComplete supplied by the user.
- * There is at most one active seekTo processed at any time. If there is a to-be-completed
- * seekTo, new seekTo requests will be queued in such a way that only the last request
- * is kept. When current seekTo is completed, the queued request will be processed if
- * that request is different from just-finished seekTo operation, i.e., the requested
- * position or mode is different.
- *
- * @param msec the offset in milliseconds from the start to seek to.
- * When seeking to the given time position, there is no guarantee that the data source
- * has a frame located at the position. When this happens, a frame nearby will be rendered.
- * If msec is negative, time position zero will be used.
- * If msec is larger than duration, duration will be used.
- * @param mode the mode indicating where exactly to seek to.
- * Use {@link #SEEK_PREVIOUS_SYNC} if one wants to seek to a sync frame
- * that has a timestamp earlier than or the same as msec. Use
- * {@link #SEEK_NEXT_SYNC} if one wants to seek to a sync frame
- * that has a timestamp later than or the same as msec. Use
- * {@link #SEEK_CLOSEST_SYNC} if one wants to seek to a sync frame
- * that has a timestamp closest to or the same as msec. Use
- * {@link #SEEK_CLOSEST} if one wants to seek to a frame that may
- * or may not be a sync frame but is closest to or the same as msec.
- * {@link #SEEK_CLOSEST} often has larger performance overhead compared
- * to the other options if there is no sync frame located at msec.
- * @throws IllegalStateException if the internal player engine has not been
- * initialized
- * @throws IllegalArgumentException if the mode is invalid.
- */
@Override
public Object seekTo(final long msec, @SeekMode int mode) {
return addTask(new Task(CALL_COMPLETED_SEEK_TO, true) {
@@ -1090,7 +1004,23 @@
+ Integer.MIN_VALUE);
posMs = Integer.MIN_VALUE;
}
+
+ synchronized (mTaskLock) {
+ if (mIsPreviousCommandSeekTo
+ && mPreviousSeekPos == posMs
+ && mPreviousSeekMode == mode) {
+ throw new CommandSkippedException(
+ "same as previous seekTo");
+ }
+ }
+
_seekTo(posMs, mode);
+
+ synchronized (mTaskLock) {
+ mIsPreviousCommandSeekTo = true;
+ mPreviousSeekPos = posMs;
+ mPreviousSeekMode = mode;
+ }
}
});
}
@@ -1124,7 +1054,7 @@
return new MediaTimestamp(
getCurrentPosition() * 1000L,
System.nanoTime(),
- isPlaying() ? getPlaybackParams().getSpeed() : 0.f);
+ getState() == PLAYER_STATE_PLAYING ? getPlaybackParams().getSpeed() : 0.f);
} catch (IllegalStateException e) {
return null;
}
@@ -1152,6 +1082,11 @@
mNextSourceState = NEXT_SOURCE_STATE_INIT;
}
+ synchronized (mTaskLock) {
+ mPendingTasks.clear();
+ mIsPreviousCommandSeekTo = false;
+ }
+
stayAwake(false);
_reset();
// make sure none of the listeners get called anymore
@@ -1748,6 +1683,12 @@
case MEDIA_SEEK_COMPLETE:
{
synchronized (mTaskLock) {
+ if (!mPendingTasks.isEmpty()
+ && mPendingTasks.get(0).mMediaCallType != CALL_COMPLETED_SEEK_TO
+ && getState() == PLAYER_STATE_PLAYING) {
+ mIsPreviousCommandSeekTo = false;
+ }
+
if (mCurrentTask != null
&& mCurrentTask.mMediaCallType == CALL_COMPLETED_SEEK_TO
&& mCurrentTask.mNeedToWaitForEventToComplete) {
@@ -3150,6 +3091,12 @@
mDSD = mCurrentDSD;
}
+ if (mMediaCallType != CALL_COMPLETED_SEEK_TO) {
+ synchronized (mTaskLock) {
+ mIsPreviousCommandSeekTo = false;
+ }
+ }
+
// TODO: Make native implementations asynchronous and let them send notifications.
if (!mNeedToWaitForEventToComplete || status != CALL_STATUS_NO_ERROR) {
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 520077f..8e30455 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -598,20 +598,6 @@
process_media_player_call( env, thiz, mp->pause(), NULL, NULL );
}
-static jboolean
-android_media_MediaPlayer2_isPlaying(JNIEnv *env, jobject thiz)
-{
- sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
- if (mp == NULL ) {
- jniThrowException(env, "java/lang/IllegalStateException", NULL);
- return JNI_FALSE;
- }
- const jboolean is_playing = mp->isPlaying();
-
- ALOGV("isPlaying: %d", is_playing);
- return is_playing;
-}
-
static void
android_media_MediaPlayer2_setPlaybackParams(JNIEnv *env, jobject thiz, jobject params)
{
@@ -1447,7 +1433,6 @@
{"getSyncParams", "()Landroid/media/SyncParams;", (void *)android_media_MediaPlayer2_getSyncParams},
{"_seekTo", "(JI)V", (void *)android_media_MediaPlayer2_seekTo},
{"_pause", "()V", (void *)android_media_MediaPlayer2_pause},
- {"isPlaying", "()Z", (void *)android_media_MediaPlayer2_isPlaying},
{"getCurrentPosition", "()J", (void *)android_media_MediaPlayer2_getCurrentPosition},
{"getDuration", "()J", (void *)android_media_MediaPlayer2_getDuration},
{"_release", "()V", (void *)android_media_MediaPlayer2_release},