Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 03f51393 authored by Jean-Michel Trivi's avatar Jean-Michel Trivi
Browse files

Offload support for android.media.AudioTrack

Unhide AudioManager method to query whether a given
  format can be offloaded on the device.
Unhide AudioTrack.Builder method to request offload.
Unhide callback for new AudioTrack events and
  associated methods.
Implement registration and callback for stream events.
JNI: configure native AudioTrack for offload.
Remove unused imports.

Bug 113352077
Bug 86837964
Test: "make offline-sdk-docs" and "adb shell clof --source /sdcard/Music/my.mp3"

Change-Id: I565668913076a57ea2c444f1f9268b21024ccde0
parent 2d8edbc9
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -22662,6 +22662,7 @@ package android.media {
    method public boolean isBluetoothScoOn();
    method public boolean isMicrophoneMute();
    method public boolean isMusicActive();
    method public static boolean isOffloadedPlaybackSupported(android.media.AudioFormat);
    method public boolean isSpeakerphoneOn();
    method public boolean isStreamMute(int);
    method public boolean isVolumeFixed();
@@ -22983,6 +22984,7 @@ package android.media {
    method public int getUnderrunCount();
    method public void pause() throws java.lang.IllegalStateException;
    method public void play() throws java.lang.IllegalStateException;
    method public void registerStreamEventCallback(java.util.concurrent.Executor, android.media.AudioTrack.StreamEventCallback);
    method public void release();
    method public int reloadStaticData();
    method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener);
@@ -23003,6 +23005,7 @@ package android.media {
    method public deprecated int setStereoVolume(float, float);
    method public int setVolume(float);
    method public void stop() throws java.lang.IllegalStateException;
    method public void unregisterStreamEventCallback(android.media.AudioTrack.StreamEventCallback);
    method public int write(byte[], int, int);
    method public int write(byte[], int, int, int);
    method public int write(short[], int, int);
@@ -23036,6 +23039,7 @@ package android.media {
    method public android.media.AudioTrack.Builder setAudioAttributes(android.media.AudioAttributes) throws java.lang.IllegalArgumentException;
    method public android.media.AudioTrack.Builder setAudioFormat(android.media.AudioFormat) throws java.lang.IllegalArgumentException;
    method public android.media.AudioTrack.Builder setBufferSizeInBytes(int) throws java.lang.IllegalArgumentException;
    method public android.media.AudioTrack.Builder setOffloadedPlayback(boolean);
    method public android.media.AudioTrack.Builder setPerformanceMode(int);
    method public android.media.AudioTrack.Builder setSessionId(int) throws java.lang.IllegalArgumentException;
    method public android.media.AudioTrack.Builder setTransferMode(int) throws java.lang.IllegalArgumentException;
@@ -23059,6 +23063,13 @@ package android.media {
    method public default void onRoutingChanged(android.media.AudioRouting);
  }
  public static abstract class AudioTrack.StreamEventCallback {
    ctor public AudioTrack.StreamEventCallback();
    method public void onDataRequest(android.media.AudioTrack, int);
    method public void onPresentationEnded(android.media.AudioTrack);
    method public void onTearDown(android.media.AudioTrack);
  }
  public class CamcorderProfile {
    method public static android.media.CamcorderProfile get(int);
    method public static android.media.CamcorderProfile get(int, int);
+3 −4
Original line number Diff line number Diff line
@@ -128,9 +128,8 @@ static inline int audioFormatFromNative(audio_format_t nativeFormat)
        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_AAC_XHE:
        return ENCODING_AAC_XHE;
    case AUDIO_FORMAT_AC4:
        return ENCODING_AC4;
    case AUDIO_FORMAT_E_AC3_JOC:
+40 −27
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include <media/AudioSystem.h>
#include <media/AudioTrack.h>

#include <android-base/macros.h>
#include <binder/MemoryHeapBase.h>
#include <binder/MemoryBase.h>

@@ -134,41 +135,53 @@ static void audioCallback(int event, void* user, void *info) {
        callbackInfo->busy = true;
    }

    // used as default argument when event callback doesn't have any, or number of
    // frames for EVENT_CAN_WRITE_MORE_DATA
    int arg = 0;
    bool postEvent = false;
    switch (event) {
    // Offload only events
    case AudioTrack::EVENT_STREAM_END:
    case AudioTrack::EVENT_MORE_DATA:
    // a.k.a. tear down
    case AudioTrack::EVENT_NEW_IAUDIOTRACK:
    case AudioTrack::EVENT_CAN_WRITE_MORE_DATA:
        // this event will read the info return parameter of the callback:
        // for JNI offload, use the returned size to indicate:
        // 1/ no data is returned through callback, as it's all done through write()
        // 2/ do not wait as AudioTrack does when it receives 0 bytes
        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();
            AudioTrack::Buffer* pBuffer = (AudioTrack::Buffer*) info;
            const size_t availableForWrite = pBuffer->size;
            arg = availableForWrite > INT32_MAX ? INT32_MAX : (int) availableForWrite;
            pBuffer->size = 0;
        }
        FALLTHROUGH_INTENDED;
    case AudioTrack::EVENT_STREAM_END:
    case AudioTrack::EVENT_NEW_IAUDIOTRACK: // a.k.a. tear down
        if (callbackInfo->isOffload) {
            postEvent = true;
        }
        } break;
        break;

    // PCM and offload events
    case AudioTrack::EVENT_MARKER:
    case AudioTrack::EVENT_NEW_POS: {
    case AudioTrack::EVENT_NEW_POS:
        postEvent = true;
        break;
    default:
        // event will not be posted
        break;
    }

    if (postEvent) {
        JNIEnv *env = AndroidRuntime::getJNIEnv();
        if (user != NULL && env != NULL) {
        if (env != NULL) {
            env->CallStaticVoidMethod(
                    callbackInfo->audioTrack_class,
                    javaAudioTrackFields.postNativeEventInJava,
                    callbackInfo->audioTrack_ref, event, 0,0, NULL);
                    callbackInfo->audioTrack_ref, event, arg, 0, NULL);
            if (env->ExceptionCheck()) {
                env->ExceptionDescribe();
                env->ExceptionClear();
            }
        }
        } break;
    }

    {
@@ -215,10 +228,10 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job
        jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession,
        jlong nativeAudioTrack, jboolean offload) {

    ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d"
        "nativeAudioTrack=0x%" PRIX64,
    ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d,"
        " nativeAudioTrack=0x%" PRIX64 ", offload=%d",
        jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes,
        nativeAudioTrack);
        nativeAudioTrack, offload);

    sp<AudioTrack> lpTrack = 0;

@@ -318,7 +331,7 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job
        lpJniStorage->mCallbackData.busy = false;

        audio_offload_info_t offloadInfo;
        if (offload) {
        if (offload == JNI_TRUE) {
            offloadInfo = AUDIO_INFO_INITIALIZER;
            offloadInfo.format = format;
            offloadInfo.sample_rate = sampleRateInHertz;
@@ -331,23 +344,23 @@ android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, job
        status_t status = NO_ERROR;
        switch (memoryMode) {
        case MODE_STREAM:

            status = lpTrack->set(
                    AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
                    sampleRateInHertz,
                    format,// word length, PCM
                    nativeChannelMask,
                    frameCount,
                    AUDIO_OUTPUT_FLAG_NONE,
                    offload ? 0 : frameCount,
                    offload ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD : AUDIO_OUTPUT_FLAG_NONE,
                    audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
                    0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
                    0,// shared mem
                    true,// thread can call Java
                    sessionId,// audio session ID
                    AudioTrack::TRANSFER_SYNC,
                    offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK : AudioTrack::TRANSFER_SYNC,
                    offload ? &offloadInfo : NULL,
                    -1, -1,                       // default uid, pid values
                    paa);

            break;

        case MODE_STATIC:
+1 −0
Original line number Diff line number Diff line
@@ -1068,6 +1068,7 @@ public final class AudioFormat implements Parcelable {
        ENCODING_E_AC3_JOC,
        ENCODING_DTS,
        ENCODING_DTS_HD,
        ENCODING_MP3,
        ENCODING_IEC61937,
        ENCODING_AAC_HE_V1,
        ENCODING_AAC_HE_V2,
+10 −4
Original line number Diff line number Diff line
@@ -1379,14 +1379,20 @@ public class AudioManager {
    //====================================================================
    // Offload query
    /**
     * @hide
     * 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.
     * <p>Offloaded playback is the feature where the decoding and playback of an audio stream
     * is not competing with other software resources. In general, it is supported by dedicated
     * hardware, such as audio DSPs.
     * <p>Note that this query only provides information about the support of an audio format,
     * it does not indicate whether the resources necessary for the offloaded playback are
     * available at that instant.
     * @param format the audio format (codec, sample rate, channels) being checked.
     * @return true if the given audio format can be offloaded.
     */
    public boolean isOffloadedPlaybackSupported(@NonNull AudioFormat format) {
    public static boolean isOffloadedPlaybackSupported(@NonNull AudioFormat format) {
        if (format == null) {
            throw new IllegalArgumentException("Illegal null AudioFormat");
        }
        return AudioSystem.isOffloadSupported(format);
    }

Loading