Commit d952c252 authored by Thomas Guillem's avatar Thomas Guillem

Remove AudioTrack Java and use AudioTrack by default

There is a new audiotrack module that use AudioTrack API by JNI.
Use it per default because of the problems we have with Open Sles.

AudioTrack (Java) and AudioTrack (Native) are now merged.
parent f1c96170
......@@ -9,7 +9,7 @@ LOCAL_SRC_FILES += libvlcjni-equalizer.c
LOCAL_SRC_FILES += libvlcjni-vlcobject.c
LOCAL_SRC_FILES += java_event_thread.c
LOCAL_SRC_FILES += libvlcjni-media.c libvlcjni-medialist.c libvlcjni-mediadiscoverer.c
LOCAL_SRC_FILES += aout.c vout.c native_crash_handler.c thumbnailer.c
LOCAL_SRC_FILES += vout.c native_crash_handler.c thumbnailer.c
ifneq ($(APP_PLATFORM),android-21)
# compat functions not needed after android-21
LOCAL_SRC_FILES += compat/pthread-condattr.c compat/pthread-rwlocks.c
......
/*****************************************************************************
* aout.c
*****************************************************************************
* Copyright © 2011-2012 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.
*****************************************************************************/
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include <jni.h>
#include <vlc/vlc.h>
#include "aout.h"
#define LOG_TAG "VLC/JNI/aout"
#include "log.h"
// An audio frame will contain FRAME_SIZE samples
#define FRAME_SIZE (4096*2)
typedef struct
{
jobject j_libVlc; /// Pointer to the LibVLC Java object
jmethodID play; /// Java method to play audio buffers
jbyteArray buffer; /// Raw audio data to be played
} aout_sys_t;
#define THREAD_NAME "jni_aout"
extern int jni_attach_thread(JNIEnv **env, const char *thread_name);
extern void jni_detach_thread();
int aout_open(void **opaque, char *format, unsigned *rate, unsigned *nb_channels)
{
LOGI ("Opening the JNI audio output");
aout_sys_t *p_sys = calloc (1, sizeof (*p_sys));
if (!p_sys)
goto enomem;
p_sys->j_libVlc = *opaque; // Keep a reference to our Java object
*opaque = (void*) p_sys; // The callback will need aout_sys_t
LOGI ("Parameters: %u channels, FOURCC '%4.4s', sample rate: %uHz",
*nb_channels, format, *rate);
JNIEnv *p_env;
if (jni_attach_thread (&p_env, THREAD_NAME) != 0)
{
LOGE("Could not attach the display thread to the JVM !");
goto eattach;
}
// Call the init function.
jclass cls = (*p_env)->GetObjectClass (p_env, p_sys->j_libVlc);
jmethodID methodIdInitAout = (*p_env)->GetMethodID (p_env, cls,
"initAout", "(III)V");
if (!methodIdInitAout)
{
LOGE ("Method initAout() could not be found!");
goto error;
}
LOGV ("Number of channels forced to 2, number of samples to %d", FRAME_SIZE);
*nb_channels = 2;
int aout_rate = *rate;
while (1) {
(*p_env)->CallVoidMethod (p_env, p_sys->j_libVlc, methodIdInitAout,
aout_rate, *nb_channels, FRAME_SIZE);
if ((*p_env)->ExceptionCheck (p_env) == 0) {
*rate = aout_rate;
break;
}
if (aout_rate <= 0) {
LOGE ("initAout failed, invalid sample rate %dHz", aout_rate);
} else if (aout_rate != 44100) {
if (aout_rate < 4000) {
do {
aout_rate *= 2;
} while (aout_rate < 4000);
} else if (aout_rate > 48000) {
do {
aout_rate /= 2;
} while (aout_rate > 48000);
} else {
aout_rate = 44100;
}
LOGE ("initAout failed, try next sample rate %dHz", aout_rate);
(*p_env)->ExceptionClear (p_env);
continue;
}
LOGE ("Unable to create audio player!");
#ifndef NDEBUG
(*p_env)->ExceptionDescribe (p_env);
#endif
(*p_env)->ExceptionClear (p_env);
goto error;
}
/* Create a new byte array to store the audio data. */
jbyteArray buffer = (*p_env)->NewByteArray (p_env,
*nb_channels *
FRAME_SIZE *
sizeof (uint16_t) /* =2 */);
if (buffer == NULL)
{
LOGE ("Could not allocate the Java byte array to store the audio data!");
goto error;
}
/* Use a global reference to not reallocate memory each time we run
the play function. */
p_sys->buffer = (*p_env)->NewGlobalRef (p_env, buffer);
/* The local reference is no longer useful. */
(*p_env)->DeleteLocalRef (p_env, buffer);
if (p_sys->buffer == NULL)
{
LOGE ("Could not create the global reference!");
goto error;
}
// Get the play methodId
p_sys->play = (*p_env)->GetMethodID (p_env, cls, "playAudio", "([BI)V");
assert (p_sys->play != NULL);
jni_detach_thread ();
return 0;
error:
jni_detach_thread ();
eattach:
*opaque = NULL;
free (p_sys);
enomem:
return -1;
}
/**
* Play an audio frame
**/
void aout_play(void *opaque, const void *samples, unsigned count, int64_t pts)
{
aout_sys_t *p_sys = opaque;
JNIEnv *p_env;
/* How ugly: we constantly attach/detach this thread to/from the JVM
* because it will be killed before aout_close is called.
* aout_close will actually be called in an different thread!
*/
jni_attach_thread (&p_env, THREAD_NAME);
(*p_env)->SetByteArrayRegion (p_env, p_sys->buffer, 0,
2 /*nb_channels*/ * count * sizeof (uint16_t),
(jbyte*) samples);
if ((*p_env)->ExceptionCheck (p_env))
{
// This can happen if for some reason the size of the input buffer
// is larger than the size of the output buffer
LOGE ("An exception occurred while calling SetByteArrayRegion");
(*p_env)->ExceptionDescribe (p_env);
(*p_env)->ExceptionClear (p_env);
return;
}
(*p_env)->CallVoidMethod (p_env, p_sys->j_libVlc, p_sys->play,
p_sys->buffer,
2 /*nb_channels*/ * count * sizeof (uint16_t),
FRAME_SIZE);
// FIXME: check for errors
jni_detach_thread ();
}
void aout_pause(void *opaque, int64_t pts)
{
LOGI ("Pausing audio output");
aout_sys_t *p_sys = opaque;
assert(p_sys);
JNIEnv *p_env;
jni_attach_thread (&p_env, THREAD_NAME);
// Call the pause function.
jclass cls = (*p_env)->GetObjectClass (p_env, p_sys->j_libVlc);
jmethodID methodIdPauseAout = (*p_env)->GetMethodID (p_env, cls, "pauseAout", "()V");
if (!methodIdPauseAout)
LOGE ("Method pauseAout() could not be found!");
(*p_env)->CallVoidMethod (p_env, p_sys->j_libVlc, methodIdPauseAout);
if ((*p_env)->ExceptionCheck (p_env))
{
LOGE ("Unable to pause audio player!");
#ifndef NDEBUG
(*p_env)->ExceptionDescribe (p_env);
#endif
(*p_env)->ExceptionClear (p_env);
}
jni_detach_thread ();
}
void aout_close(void *opaque)
{
LOGI ("Closing audio output");
aout_sys_t *p_sys = opaque;
assert(p_sys);
assert(p_sys->buffer);
JNIEnv *p_env;
jni_attach_thread (&p_env, THREAD_NAME);
// Call the close function.
jclass cls = (*p_env)->GetObjectClass (p_env, p_sys->j_libVlc);
jmethodID methodIdCloseAout = (*p_env)->GetMethodID (p_env, cls, "closeAout", "()V");
if (!methodIdCloseAout)
LOGE ("Method closeAout() could not be found!");
(*p_env)->CallVoidMethod (p_env, p_sys->j_libVlc, methodIdCloseAout);
if ((*p_env)->ExceptionCheck (p_env))
{
LOGE ("Unable to close audio player!");
#ifndef NDEBUG
(*p_env)->ExceptionDescribe (p_env);
#endif
(*p_env)->ExceptionClear (p_env);
}
(*p_env)->DeleteGlobalRef (p_env, p_sys->buffer);
(*p_env)->DeleteGlobalRef (p_env, p_sys->j_libVlc);
jni_detach_thread ();
free (p_sys);
}
int aout_get_native_sample_rate(void)
{
JNIEnv *p_env;
jni_attach_thread (&p_env, THREAD_NAME);
jclass cls = (*p_env)->FindClass (p_env, "android/media/AudioTrack");
jmethodID method = (*p_env)->GetStaticMethodID (p_env, cls, "getNativeOutputSampleRate", "(I)I");
int sample_rate = (*p_env)->CallStaticIntMethod (p_env, cls, method, 3); // AudioManager.STREAM_MUSIC
jni_detach_thread ();
return sample_rate;
}
/*****************************************************************************
* aout.h
*****************************************************************************
* Copyright © 2011-2012 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.
*****************************************************************************/
#ifndef LIBVLCJNI_AOUT_H
#define LIBVLCJNI_AOUT_H
#include <stdint.h>
#define AOUT_AUDIOTRACK_JAVA 0
#define AOUT_AUDIOTRACK 1
#define AOUT_OPENSLES 2
int aout_open(void **opaque, char *format, unsigned *rate, unsigned *nb_channels);
void aout_play(void *opaque, const void *samples, unsigned count, int64_t pts);
void aout_pause(void *opaque, int64_t pts);
void aout_close(void *opaque);
#endif // LIBVLCJNI_AOUT_H
......@@ -36,7 +36,6 @@
#include <android/api-level.h>
#include "libvlcjni.h"
#include "aout.h"
#include "vout.h"
#include "utils.h"
#include "native_crash_handler.h"
......@@ -45,6 +44,9 @@
#define VOUT_OPENGLES2 1
#define VOUT_ANDROID_WINDOW 2
#define AOUT_AUDIOTRACK 0
#define AOUT_OPENSLES 1
#define LOG_TAG "VLC/JNI/main"
#include "log.h"
......@@ -498,6 +500,7 @@ void Java_org_videolan_libvlc_LibVLC_setEventHandler(JNIEnv *env, jobject thiz,
void Java_org_videolan_libvlc_LibVLC_playMRL(JNIEnv *env, jobject thiz,
jstring mrl, jobjectArray mediaOptions)
{
jclass cls;
/* Release previous media player, if any */
releaseMediaPlayer(env, thiz);
......@@ -508,16 +511,6 @@ void Java_org_videolan_libvlc_LibVLC_playMRL(JNIEnv *env, jobject thiz,
libvlc_media_player_set_video_title_display(mp, libvlc_position_disable, 0);
jobject myJavaLibVLC = (*env)->NewGlobalRef(env, thiz); // freed in aout_close
// If AOUT_AUDIOTRACK_JAVA, use amem
jclass cls = (*env)->GetObjectClass(env, thiz);
jmethodID methodId = (*env)->GetMethodID(env, cls, "getAout", "()I");
if ( (*env)->CallIntMethod(env, thiz, methodId) == AOUT_AUDIOTRACK_JAVA )
{
libvlc_audio_set_callbacks(mp, aout_play, aout_pause, NULL, NULL, NULL,
(void*) myJavaLibVLC);
libvlc_audio_set_format_callbacks(mp, aout_open, aout_close);
}
/* Connect the event manager */
libvlc_event_manager_t *ev = libvlc_media_player_event_manager(mp);
static const libvlc_event_type_t mp_events[] = {
......@@ -818,3 +811,15 @@ int jni_GetWindowSize(int *width, int *height)
pthread_mutex_unlock(&vout_android_lock);
return 0;
}
/* used by opensles module */
int aout_get_native_sample_rate(void)
{
JNIEnv *p_env;
jni_attach_thread (&p_env, THREAD_NAME);
jclass cls = (*p_env)->FindClass (p_env, "android/media/AudioTrack");
jmethodID method = (*p_env)->GetStaticMethodID (p_env, cls, "getNativeOutputSampleRate", "(I)I");
int sample_rate = (*p_env)->CallStaticIntMethod (p_env, cls, method, 3); // AudioManager.STREAM_MUSIC
jni_detach_thread ();
return sample_rate;
}
/*****************************************************************************
* Aout.java
*****************************************************************************
* Copyright © 2011-2012 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 android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.util.Log;
public class AudioOutput {
/**
* Java side of the audio output module for Android.
* Uses an AudioTrack to play decoded audio buffers.
*
* TODO Use MODE_STATIC instead of MODE_STREAM with a MemoryFile (ashmem)
*/
public AudioOutput() {
}
private AudioTrack mAudioTrack;
private static final String TAG = "LibVLC/aout";
public void init(int sampleRateInHz, int channels, int samples) {
Log.d(TAG, sampleRateInHz + ", " + channels + ", " + samples + "=>" + channels * samples);
int minBufferSize = AudioTrack.getMinBufferSize(sampleRateInHz,
AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.ENCODING_PCM_16BIT);
mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,
sampleRateInHz,
AudioFormat.CHANNEL_OUT_STEREO,
AudioFormat.ENCODING_PCM_16BIT,
Math.max(minBufferSize, channels * samples * 2),
AudioTrack.MODE_STREAM);
}
public void release() {
if (mAudioTrack != null) {
mAudioTrack.release();
}
mAudioTrack = null;
}
public void playBuffer(byte[] audioData, int bufferSize) {
if (mAudioTrack.getState() == AudioTrack.STATE_UNINITIALIZED)
return;
if (mAudioTrack.write(audioData, 0, bufferSize) != bufferSize) {
Log.w(TAG, "Could not write all the samples to the audio device");
}
mAudioTrack.play();
}
public void pause() {
mAudioTrack.pause();
}
}
......@@ -31,9 +31,8 @@ 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 AOUT_AUDIOTRACK = 0;
public static final int AOUT_OPENSLES = 1;
public static final int VOUT_ANDROID_SURFACE = 0;
public static final int VOUT_OPEGLES2 = 1;
......@@ -73,8 +72,6 @@ public class LibVLC {
private StringBuffer mDebugLogBuffer;
private boolean mIsBufferingLog = false;
private AudioOutput mAout;
/** Keep screen bright */
//private WakeLock mWakeLock;
......@@ -84,7 +81,7 @@ public class LibVLC {
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 aout = AOUT_AUDIOTRACK;
private int vout = VOUT_ANDROID_SURFACE;
private boolean timeStretching = false;
private int deblocking = -1;
......@@ -196,7 +193,6 @@ public class LibVLC {
* It is private because this class is a singleton.
*/
private LibVLC() {
mAout = new AudioOutput();
}
/**
......@@ -363,10 +359,10 @@ public class LibVLC {
}
public void setAout(int aout) {
if (aout < 0)
this.aout = LibVlcUtil.isICSOrLater() ? AOUT_OPENSLES : AOUT_AUDIOTRACK_JAVA;
if (aout == AOUT_OPENSLES && LibVlcUtil.isICSOrLater())
this.aout = AOUT_OPENSLES;
else
this.aout = aout;
this.aout = AOUT_AUDIOTRACK;
}
public int getVout() {
......@@ -518,41 +514,6 @@ public class LibVLC {
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 an MRL directly.
*
......
......@@ -268,9 +268,8 @@
<string name="advanced_prefs_category">Advanced</string>
<string name="aout">Audio output</string>
<string name="aout_summary">Change the method that VLC uses to output audio.</string>
<string name="aout_audiotrack">AudioTrack (native)</string>
<string name="aout_audiotrack_java">AudioTrack (Java)</string>
<string name="aout_opensles">OpenSL ES</string>
<string name="aout_audiotrack" translatable="false">AudioTrack</string>
<string name="aout_opensles" translatable="false">OpenSL ES</string>
<string name="vout">Video output</string>
<string name="vout_summary">Change the method that VLC uses to output video.</string>
<string name="vout_android_surface">Android surface</string>
......@@ -476,23 +475,13 @@
<item>Windows-1258</item>
</string-array>
<string-array name="aouts_froyo">
<item>@string/aout_audiotrack_java</item>
<item>@string/aout_audiotrack</item>
</string-array>
<string-array name="aouts">
<item>@string/aout_audiotrack_java</item>
<item>@string/aout_audiotrack</item>
<item>@string/aout_opensles</item>
</string-array>
<string-array name="aouts_values_froyo" translatable="false">
<item>0</item>
<item>1</item>
</string-array>
<string-array name="aouts_values" translatable="false">
<item>0</item>
<item>1</item>
<item>2</item>
</string-array>
<string-array name="chroma_formats_values" translatable="false">
......
......@@ -108,7 +108,8 @@
</PreferenceCategory>
</PreferenceScreen>
<PreferenceScreen android:title="@string/advanced_prefs_category" >
<PreferenceCategory android:title="@string/advanced_prefs_category" >
<PreferenceCategory android:key="advanced_prefs_group"
android:title="@string/advanced_prefs_category" >
<ListPreference
android:key="aout"
android:summary="@string/aout_summary"
......
......@@ -51,6 +51,7 @@ import android.preference.Preference;
import android.preference.Preference.OnPreferenceChangeListener;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceGroup;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.text.format.DateFormat;
......@@ -237,12 +238,25 @@ public class PreferencesActivity extends PreferenceActivity implements OnSharedP
// Audio output
ListPreference aoutPref = (ListPreference) findPreference("aout");
int aoutEntriesId = LibVlcUtil.isGingerbreadOrLater() ? R.array.aouts : R.array.aouts_froyo;
int aoutEntriesIdValues = LibVlcUtil.isGingerbreadOrLater() ? R.array.aouts_values : R.array.aouts_values_froyo;
aoutPref.setEntries(aoutEntriesId);
aoutPref.setEntryValues(aoutEntriesIdValues);
if (aoutPref.getValue() == null)
aoutPref.setValue("0"/*AOUT_AUDIOTRACK_JAVA*/);
if (LibVlcUtil.isICSOrLater()) {
int aoutEntriesId = R.array.aouts;
int aoutEntriesIdValues = R.array.aouts_values;
aoutPref.setEntries(aoutEntriesId);
aoutPref.setEntryValues(aoutEntriesIdValues);
final String value = aoutPref.getValue();
if (value == null)
aoutPref.setValue(String.valueOf(LibVLC.AOUT_AUDIOTRACK));
else {
/* number of entries decreased, handle old values */
final int intValue = Integer.parseInt(value);
if (intValue != LibVLC.AOUT_AUDIOTRACK && intValue != LibVLC.AOUT_OPENSLES)
aoutPref.setValue(String.valueOf(LibVLC.AOUT_AUDIOTRACK));
}
} else {
/* only audiotrack before ics */
PreferenceGroup group = (PreferenceGroup) findPreference("advanced_prefs_group");
group.removePreference(aoutPref);
}
// Video output
ListPreference voutPref = (ListPreference) findPreference("vout");
int voutEntriesId = LibVlcUtil.isGingerbreadOrLater() ? R.array.vouts : R.array.vouts_froyo;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment