Merge "AudioTrack: support for offloaded playback"

This commit is contained in:
TreeHugger Robot
2018-01-20 05:48:37 +00:00
committed by Android (Google) Code Review
7 changed files with 292 additions and 51 deletions

View File

@@ -33,6 +33,8 @@
#define ENCODING_AAC_HE_V2 12
#define ENCODING_IEC61937 13
#define ENCODING_DOLBY_TRUEHD 14
#define ENCODING_AAC_ELD 15
#define ENCODING_AAC_XHE 16
#define ENCODING_INVALID 0
#define ENCODING_DEFAULT 1
@@ -71,6 +73,10 @@ static inline audio_format_t audioFormatToNative(int audioFormat)
return AUDIO_FORMAT_DOLBY_TRUEHD;
case ENCODING_IEC61937:
return AUDIO_FORMAT_IEC61937;
case ENCODING_AAC_ELD:
return AUDIO_FORMAT_AAC_ELD;
case ENCODING_AAC_XHE:
return AUDIO_FORMAT_AAC; // FIXME temporary value, needs addition of xHE-AAC
case ENCODING_DEFAULT:
return AUDIO_FORMAT_DEFAULT;
default:
@@ -114,6 +120,11 @@ static inline int audioFormatFromNative(audio_format_t nativeFormat)
return ENCODING_IEC61937;
case AUDIO_FORMAT_DOLBY_TRUEHD:
return ENCODING_DOLBY_TRUEHD;
case AUDIO_FORMAT_AAC_ELD:
return ENCODING_AAC_ELD;
// FIXME needs addition of AUDIO_FORMAT_AAC_XHE
//case AUDIO_FORMAT_AAC_XHE:
// return ENCODING_AAC_XHE;
case AUDIO_FORMAT_DEFAULT:
return ENCODING_DEFAULT;
default:
@@ -121,6 +132,25 @@ static inline int audioFormatFromNative(audio_format_t nativeFormat)
}
}
// This function converts Java channel masks to a native channel mask.
// validity should be checked with audio_is_output_channel().
static inline audio_channel_mask_t nativeChannelMaskFromJavaChannelMasks(
jint channelPositionMask, jint channelIndexMask)
{
// 0 is the java android.media.AudioFormat.CHANNEL_INVALID value
if (channelIndexMask != 0) { // channel index mask takes priority
// To convert to a native channel mask, the Java channel index mask
// requires adding the index representation.
return audio_channel_mask_from_representation_and_bits(
AUDIO_CHANNEL_REPRESENTATION_INDEX,
channelIndexMask);
}
// To convert to a native channel mask, the Java channel position mask
// requires a shift by 2 to skip the two deprecated channel
// configurations "default" and "mono".
return (audio_channel_mask_t)((uint32_t)channelPositionMask >> 2);
}
static inline audio_channel_mask_t outChannelMaskToNative(int channelMask)
{
switch (channelMask) {

View File

@@ -1770,6 +1770,24 @@ android_media_AudioSystem_getStreamVolumeDB(JNIEnv *env, jobject thiz,
(audio_devices_t)device);
}
static jboolean
android_media_AudioSystem_isOffloadSupported(JNIEnv *env, jobject thiz,
jint encoding, jint sampleRate, jint channelMask, jint channelIndexMask)
{
audio_offload_info_t format = AUDIO_INFO_INITIALIZER;
format.format = (audio_format_t) audioFormatToNative(encoding);
format.sample_rate = (uint32_t) sampleRate;
format.channel_mask = nativeChannelMaskFromJavaChannelMasks(channelMask, channelIndexMask);
format.stream_type = AUDIO_STREAM_MUSIC;
format.has_video = false;
format.is_streaming = false;
// offload duration unknown at this point:
// client side code cannot access "audio.offload.min.duration.secs" property to make a query
// agnostic of duration, so using acceptable estimate of 2mn
format.duration_us = 120 * 1000000;
return AudioSystem::isOffloadSupported(format);
}
// ----------------------------------------------------------------------------
static const JNINativeMethod gMethods[] = {
@@ -1823,6 +1841,7 @@ static const JNINativeMethod gMethods[] = {
(void *)android_media_AudioSystem_registerRecordingCallback},
{"systemReady", "()I", (void *)android_media_AudioSystem_systemReady},
{"getStreamVolumeDB", "(III)F", (void *)android_media_AudioSystem_getStreamVolumeDB},
{"native_is_offload_supported", "(IIII)Z", (void *)android_media_AudioSystem_isOffloadSupported},
};

View File

@@ -73,6 +73,7 @@ struct audiotrack_callback_cookie {
jobject audioTrack_ref;
bool busy;
Condition cond;
bool isOffload;
};
// keep these values in sync with AudioTrack.java
@@ -90,6 +91,7 @@ class AudioTrackJniStorage {
AudioTrackJniStorage() {
mCallbackData.audioTrack_class = 0;
mCallbackData.audioTrack_ref = 0;
mCallbackData.isOffload = false;
}
~AudioTrackJniStorage() {
@@ -132,27 +134,34 @@ static void audioCallback(int event, void* user, void *info) {
}
switch (event) {
case AudioTrack::EVENT_MARKER: {
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (user != NULL && env != NULL) {
env->CallStaticVoidMethod(
callbackInfo->audioTrack_class,
javaAudioTrackFields.postNativeEventInJava,
callbackInfo->audioTrack_ref, event, 0,0, NULL);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
// Offload only events
case AudioTrack::EVENT_STREAM_END:
case AudioTrack::EVENT_MORE_DATA:
// a.k.a. tear down
case AudioTrack::EVENT_NEW_IAUDIOTRACK:
if (callbackInfo->isOffload) {
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (user != NULL && env != NULL) {
env->CallStaticVoidMethod(
callbackInfo->audioTrack_class,
javaAudioTrackFields.postNativeEventInJava,
callbackInfo->audioTrack_ref, event, 0,0, NULL);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
}
}
} break;
// PCM and offload events
case AudioTrack::EVENT_MARKER:
case AudioTrack::EVENT_NEW_POS: {
JNIEnv *env = AndroidRuntime::getJNIEnv();
if (user != NULL && env != NULL) {
env->CallStaticVoidMethod(
callbackInfo->audioTrack_class,
javaAudioTrackFields.postNativeEventInJava,
callbackInfo->audioTrack_ref, event, 0,0, NULL);
callbackInfo->audioTrack_class,
javaAudioTrackFields.postNativeEventInJava,
callbackInfo->audioTrack_ref, event, 0,0, NULL);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
@@ -198,30 +207,12 @@ sp<AudioTrack> android_media_AudioTrack_getAudioTrack(JNIEnv* env, jobject audio
return getAudioTrack(env, audioTrackObj);
}
// This function converts Java channel masks to a native channel mask.
// validity should be checked with audio_is_output_channel().
static inline audio_channel_mask_t nativeChannelMaskFromJavaChannelMasks(
jint channelPositionMask, jint channelIndexMask)
{
if (channelIndexMask != 0) { // channel index mask takes priority
// To convert to a native channel mask, the Java channel index mask
// requires adding the index representation.
return audio_channel_mask_from_representation_and_bits(
AUDIO_CHANNEL_REPRESENTATION_INDEX,
channelIndexMask);
}
// To convert to a native channel mask, the Java channel position mask
// requires a shift by 2 to skip the two deprecated channel
// configurations "default" and "mono".
return (audio_channel_mask_t)(channelPositionMask >> 2);
}
// ----------------------------------------------------------------------------
static jint
android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa,
jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask,
jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession,
jlong nativeAudioTrack) {
jlong nativeAudioTrack, jboolean offload) {
ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d"
"nativeAudioTrack=0x%" PRIX64,
@@ -322,8 +313,19 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job
lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
// we use a weak reference so the AudioTrack object can be garbage collected.
lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
lpJniStorage->mCallbackData.isOffload = offload;
lpJniStorage->mCallbackData.busy = false;
audio_offload_info_t offloadInfo;
if (offload) {
offloadInfo = AUDIO_INFO_INITIALIZER;
offloadInfo.format = format;
offloadInfo.sample_rate = sampleRateInHertz;
offloadInfo.channel_mask = nativeChannelMask;
offloadInfo.has_video = false;
offloadInfo.stream_type = AUDIO_STREAM_MUSIC; //required for offload
}
// initialize the native AudioTrack object
status_t status = NO_ERROR;
switch (memoryMode) {
@@ -342,7 +344,7 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job
true,// thread can call Java
sessionId,// audio session ID
AudioTrack::TRANSFER_SYNC,
NULL, // default offloadInfo
offload ? &offloadInfo : NULL,
-1, -1, // default uid, pid values
paa);
break;
@@ -1234,7 +1236,7 @@ static const JNINativeMethod gMethods[] = {
{"native_stop", "()V", (void *)android_media_AudioTrack_stop},
{"native_pause", "()V", (void *)android_media_AudioTrack_pause},
{"native_flush", "()V", (void *)android_media_AudioTrack_flush},
{"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJ)I",
{"native_setup", "(Ljava/lang/Object;Ljava/lang/Object;[IIIIII[IJZ)I",
(void *)android_media_AudioTrack_setup},
{"native_finalize", "()V", (void *)android_media_AudioTrack_finalize},
{"native_release", "()V", (void *)android_media_AudioTrack_release},

View File

@@ -240,20 +240,25 @@ public final class AudioFormat implements Parcelable {
public static final int ENCODING_DTS_HD = 8;
/** Audio data format: MP3 compressed
* @hide
* TODO unhide and add to @Encoding (intentional white space
* */
public static final int ENCODING_MP3 = 9;
/** Audio data format: AAC LC compressed
* @hide
* TODO unhide and add to @Encoding (intentional white space
* */
public static final int ENCODING_AAC_LC = 10;
/** Audio data format: AAC HE V1 compressed
* @hide
* TODO unhide and add to @Encoding (intentional white space
* */
public static final int ENCODING_AAC_HE_V1 = 11;
/** Audio data format: AAC HE V2 compressed
* @hide
* TODO unhide and add to @Encoding (intentional white space
* */
public static final int ENCODING_AAC_HE_V2 = 12;
/** Audio data format: compressed audio wrapped in PCM for HDMI
* or S/PDIF passthrough.
* IEC61937 uses a stereo stream of 16-bit samples as the wrapper.
@@ -266,6 +271,16 @@ public final class AudioFormat implements Parcelable {
/** Audio data format: DOLBY TRUEHD compressed
**/
public static final int ENCODING_DOLBY_TRUEHD = 14;
/** Audio data format: AAC ELD compressed
* @hide
* TODO unhide and add to @Encoding (intentional white space
* */
public static final int ENCODING_AAC_ELD = 15;
/** Audio data format: AAC xHE compressed
* @hide
* TODO unhide and add to @Encoding (intentional white space
* */
public static final int ENCODING_AAC_XHE = 16;
/** @hide */
public static String toLogFriendlyEncoding(int enc) {
@@ -298,6 +313,10 @@ public final class AudioFormat implements Parcelable {
return "ENCODING_IEC61937";
case ENCODING_DOLBY_TRUEHD:
return "ENCODING_DOLBY_TRUEHD";
case ENCODING_AAC_ELD:
return "ENCODING_AAC_ELD";
case ENCODING_AAC_XHE:
return "ENCODING_AAC_XHE";
default :
return "invalid encoding " + enc;
}
@@ -514,6 +533,8 @@ public final class AudioFormat implements Parcelable {
case ENCODING_AAC_HE_V1:
case ENCODING_AAC_HE_V2:
case ENCODING_IEC61937:
case ENCODING_AAC_ELD:
case ENCODING_AAC_XHE:
return true;
default:
return false;
@@ -532,6 +553,13 @@ public final class AudioFormat implements Parcelable {
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_IEC61937:
//TODO not true yet (intended white space
case ENCODING_MP3:
case ENCODING_AAC_LC:
case ENCODING_AAC_HE_V1:
case ENCODING_AAC_HE_V2:
case ENCODING_AAC_ELD:
case ENCODING_AAC_XHE:
return true;
default:
return false;
@@ -556,6 +584,8 @@ public final class AudioFormat implements Parcelable {
case ENCODING_AAC_HE_V1:
case ENCODING_AAC_HE_V2:
case ENCODING_IEC61937: // wrapped in PCM but compressed
case ENCODING_AAC_ELD:
case ENCODING_AAC_XHE:
return false;
case ENCODING_INVALID:
default:
@@ -581,6 +611,8 @@ public final class AudioFormat implements Parcelable {
case ENCODING_AAC_LC:
case ENCODING_AAC_HE_V1:
case ENCODING_AAC_HE_V2:
case ENCODING_AAC_ELD:
case ENCODING_AAC_XHE:
return false;
case ENCODING_INVALID:
default:
@@ -794,14 +826,7 @@ public final class AudioFormat implements Parcelable {
/**
* Sets the data encoding format.
* @param encoding one of {@link AudioFormat#ENCODING_DEFAULT},
* {@link AudioFormat#ENCODING_PCM_8BIT},
* {@link AudioFormat#ENCODING_PCM_16BIT},
* {@link AudioFormat#ENCODING_PCM_FLOAT},
* {@link AudioFormat#ENCODING_AC3},
* {@link AudioFormat#ENCODING_E_AC3}.
* {@link AudioFormat#ENCODING_DTS},
* {@link AudioFormat#ENCODING_DTS_HD}.
* @param encoding the specified encoding or default.
* @return the same Builder instance.
* @throws java.lang.IllegalArgumentException
*/
@@ -818,6 +843,12 @@ public final class AudioFormat implements Parcelable {
case ENCODING_DTS:
case ENCODING_DTS_HD:
case ENCODING_IEC61937:
case ENCODING_MP3:
case ENCODING_AAC_LC:
case ENCODING_AAC_HE_V1:
case ENCODING_AAC_HE_V2:
case ENCODING_AAC_ELD:
case ENCODING_AAC_XHE:
mEncoding = encoding;
break;
case ENCODING_INVALID:
@@ -1016,7 +1047,7 @@ public final class AudioFormat implements Parcelable {
}
/** @hide */
@IntDef({
@IntDef(flag = false, prefix = "ENCODING", value = {
ENCODING_DEFAULT,
ENCODING_PCM_8BIT,
ENCODING_PCM_16BIT,
@@ -1025,8 +1056,8 @@ public final class AudioFormat implements Parcelable {
ENCODING_E_AC3,
ENCODING_DTS,
ENCODING_DTS_HD,
ENCODING_IEC61937
})
ENCODING_IEC61937 }
)
@Retention(RetentionPolicy.SOURCE)
public @interface Encoding {}

View File

@@ -1328,6 +1328,21 @@ public class AudioManager {
}
}
//====================================================================
// Offload query
/**
* @hide
* TODO unhide (intentional white space to attract attention:
* Returns whether offloaded playback of an audio format is supported on the device.
* Offloaded playback is where the decoding of an audio stream is not competing with other
* software resources. In general, it is supported by dedicated hardware, such as audio DSPs.
* @param format the audio format (codec, sample rate, channels) being checked.
* @return true if the given audio format can be offloaded.
*/
public static boolean isOffloadedPlaybackSupported(@NonNull AudioFormat format) {
return AudioSystem.isOffloadSupported(format);
}
//====================================================================
// Bluetooth SCO control
/**

View File

@@ -16,6 +16,7 @@
package android.media;
import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.PackageManager;
import android.media.audiopolicy.AudioMix;
@@ -818,6 +819,14 @@ public class AudioSystem
public static native float getStreamVolumeDB(int stream, int index, int device);
static boolean isOffloadSupported(@NonNull AudioFormat format) {
return native_is_offload_supported(format.getEncoding(), format.getSampleRate(),
format.getChannelMask(), format.getChannelIndexMask());
}
private static native boolean native_is_offload_supported(int encoding, int sampleRate,
int channelMask, int channelIndexMask);
// Items shared with audio service
/**

View File

@@ -24,7 +24,9 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.NioUtils;
import java.util.Collection;
import java.util.concurrent.Executor;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -185,6 +187,22 @@ public class AudioTrack extends PlayerBase
* Event id denotes when previously set update period has elapsed during playback.
*/
private static final int NATIVE_EVENT_NEW_POS = 4;
/**
* Callback for more data
* TODO only for offload
*/
private static final int NATIVE_EVENT_MORE_DATA = 0;
/**
* IAudioTrack tear down for offloaded tracks
* TODO: when received, java AudioTrack must be released
*/
private static final int NATIVE_EVENT_NEW_IAUDIOTRACK = 6;
/**
* Event id denotes when all the buffers queued in AF and HW are played
* back (after stop is called) for an offloaded track.
* TODO: not just for offload
*/
private static final int NATIVE_EVENT_STREAM_END = 7;
private final static String TAG = "android.media.AudioTrack";
@@ -540,6 +558,12 @@ public class AudioTrack extends PlayerBase
public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
int mode, int sessionId)
throws IllegalArgumentException {
this(attributes, format, bufferSizeInBytes, mode, sessionId, false /*offload*/);
}
private AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
int mode, int sessionId, boolean offload)
throws IllegalArgumentException {
super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_JAM_AUDIOTRACK);
// mState already == STATE_UNINITIALIZED
@@ -601,7 +625,8 @@ public class AudioTrack extends PlayerBase
// native initialization
int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/);
mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/,
offload);
if (initResult != SUCCESS) {
loge("Error code "+initResult+" when initializing AudioTrack.");
return; // with mState == STATE_UNINITIALIZED
@@ -681,7 +706,8 @@ public class AudioTrack extends PlayerBase
0 /*mNativeBufferSizeInBytes - NA*/,
0 /*mDataLoadMode - NA*/,
session,
nativeTrackInJavaObj);
nativeTrackInJavaObj,
false /*offload*/);
if (initResult != SUCCESS) {
loge("Error code "+initResult+" when initializing AudioTrack.");
return; // with mState == STATE_UNINITIALIZED
@@ -730,6 +756,7 @@ public class AudioTrack extends PlayerBase
* <br>If the session ID is not specified with {@link #setSessionId(int)}, a new one will
* be generated.
*/
// TODO add that offload is false by default (intended white space
public static class Builder {
private AudioAttributes mAttributes;
private AudioFormat mFormat;
@@ -737,6 +764,7 @@ public class AudioTrack extends PlayerBase
private int mSessionId = AudioManager.AUDIO_SESSION_ID_GENERATE;
private int mMode = MODE_STREAM;
private int mPerformanceMode = PERFORMANCE_MODE_NONE;
private boolean mOffload = false;
/**
* Constructs a new Builder with the default values as described above.
@@ -866,6 +894,24 @@ public class AudioTrack extends PlayerBase
return this;
}
/**
* @hide
* TODO unhide (intentional whitespace
* TODO should offload require POWER_SAVING?
* Sets whether this track will play through the offloaded audio path.
* When set to true, at build time, the audio format will be checked against
* {@link AudioManager#isOffloadedPlaybackSupported(AudioFormat)} to verify the audio format
* used by this track is supported on the device's offload path (if any).
* <br>Offload is only supported for media audio data, and therefore require that
* the usage be {@link AudioAttributes#USAGE_MEDIA}.
* @param offload true to require the offload path for playback.
* @return the same Builder instance.
*/
public @NonNull Builder setOffloadedPlayback(boolean offload) {
mOffload = offload;
return this;
}
/**
* Builds an {@link AudioTrack} instance initialized with all the parameters set
* on this <code>Builder</code>.
@@ -909,6 +955,19 @@ public class AudioTrack extends PlayerBase
.setEncoding(AudioFormat.ENCODING_DEFAULT)
.build();
}
//TODO tie offload to PERFORMANCE_MODE_POWER_SAVING?
if (mOffload) {
if (mAttributes.getUsage() != AudioAttributes.USAGE_MEDIA) {
throw new UnsupportedOperationException(
"Cannot create AudioTrack, offload requires USAGE_MEDIA");
}
if (!AudioManager.isOffloadedPlaybackSupported(mFormat)) {
throw new UnsupportedOperationException(
"Cannot create AudioTrack, offload format not supported");
}
}
try {
// If the buffer size is not specified in streaming mode,
// use a single frame for the buffer size and let the
@@ -918,7 +977,7 @@ public class AudioTrack extends PlayerBase
* mFormat.getBytesPerSample(mFormat.getEncoding());
}
final AudioTrack track = new AudioTrack(
mAttributes, mFormat, mBufferSizeInBytes, mMode, mSessionId);
mAttributes, mFormat, mBufferSizeInBytes, mMode, mSessionId, mOffload);
if (track.getState() == STATE_UNINITIALIZED) {
// release is not necessary
throw new UnsupportedOperationException("Cannot create AudioTrack");
@@ -2882,6 +2941,55 @@ public class AudioTrack extends PlayerBase
void onPeriodicNotification(AudioTrack track);
}
/**
* @hide
* TODO unhide (intentional white space to attract attention:
* Abstract class to receive event notification about the stream playback.
*/
public abstract static class StreamEventCallback {
// TODO rename if supported for non offload tracks
public void onTearDown(AudioTrack track) { }
public void onStreamPresentationEnd(AudioTrack track) { }
public void onStreamDataRequest(AudioTrack track) { }
}
private Executor mStreamEventExec;
private StreamEventCallback mStreamEventCb;
private final Object mStreamEventCbLock = new Object();
/**
* @hide
* TODO unhide (intentional white space to attract attention:
* Registers a callback for notification of stream events.
* @param executor {@link Executor} to handle the callbacks
* @param eventCallback the callback to receive the stream events
*/
public void setStreamEventCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull StreamEventCallback eventCallback) {
if (eventCallback == null) {
throw new IllegalArgumentException("Illegal null StreamEventCallback");
}
if (executor == null) {
throw new IllegalArgumentException("Illegal null Executor for the StreamEventCallback");
}
synchronized (mStreamEventCbLock) {
mStreamEventExec = executor;
mStreamEventCb = eventCallback;
}
}
/**
* @hide
* Unregisters the callback for notification of stream events, previously set
* by {@link #setStreamEventCallback(StreamEventCallback, Executor)}.
*/
public void removeStreamEventCallback() {
synchronized (mStreamEventCbLock) {
mStreamEventExec = null;
mStreamEventCb = null;
}
}
//---------------------------------------------------------
// Inner classes
//--------------------
@@ -2965,7 +3073,7 @@ public class AudioTrack extends PlayerBase
private static void postEventFromNative(Object audiotrack_ref,
int what, int arg1, int arg2, Object obj) {
//logd("Event posted from the native side: event="+ what + " args="+ arg1+" "+arg2);
AudioTrack track = (AudioTrack)((WeakReference)audiotrack_ref).get();
final AudioTrack track = (AudioTrack)((WeakReference)audiotrack_ref).get();
if (track == null) {
return;
}
@@ -2974,6 +3082,32 @@ public class AudioTrack extends PlayerBase
track.broadcastRoutingChange();
return;
}
if (what == NATIVE_EVENT_MORE_DATA || what == NATIVE_EVENT_NEW_IAUDIOTRACK
|| what == NATIVE_EVENT_STREAM_END) {
final Executor exec;
final StreamEventCallback cb;
synchronized (track.mStreamEventCbLock) {
exec = track.mStreamEventExec;
cb = track.mStreamEventCb;
}
if ((exec == null) || (cb == null)) {
return;
}
switch (what) {
case NATIVE_EVENT_MORE_DATA:
exec.execute(() -> cb.onStreamDataRequest(track));
return;
case NATIVE_EVENT_NEW_IAUDIOTRACK:
// TODO also release track as it's not longer usable
exec.execute(() -> cb.onTearDown(track));
return;
case NATIVE_EVENT_STREAM_END:
exec.execute(() -> cb.onStreamPresentationEnd(track));
return;
}
}
NativePositionEventHandlerDelegate delegate = track.mEventHandlerDelegate;
if (delegate != null) {
Handler handler = delegate.getHandler();
@@ -2995,7 +3129,8 @@ public class AudioTrack extends PlayerBase
private native final int native_setup(Object /*WeakReference<AudioTrack>*/ audiotrack_this,
Object /*AudioAttributes*/ attributes,
int[] sampleRate, int channelMask, int channelIndexMask, int audioFormat,
int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack);
int buffSizeInBytes, int mode, int[] sessionId, long nativeAudioTrack,
boolean offload);
private native final void native_finalize();