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

Commit 980d38ff authored by Jean-Michel Trivi's avatar Jean-Michel Trivi
Browse files

AudioTrack: support for offloaded playback

Surface new callbacks from native AudioTrack to
  Java through the existing callback method and
  post to the Java instance.
New AudioManager method to query whether offload
  is supported.
Allow using non-PCM format on a track built for
  offload (new option in AudioTrack.Builder)

Numerous "todo" are added for the unhide of the
  API additions and will be in a separate patch.

Bug: 63934228
Test: same as MediaPlayer2 tests

Change-Id: I98a81ffffc95c7596a39ee634dcb89bb854f3178
parent 9761f47e
Loading
Loading
Loading
Loading
+30 −0
Original line number Diff line number Diff line
@@ -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) {
+19 −0
Original line number Diff line number Diff line
@@ -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},
};


+37 −35
Original line number Diff line number Diff line
@@ -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,7 +134,12 @@ static void audioCallback(int event, void* user, void *info) {
    }

    switch (event) {
    case AudioTrack::EVENT_MARKER: {
    // 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(
@@ -146,6 +153,8 @@ static void audioCallback(int event, void* user, void *info) {
            }
        } break;

    // PCM and offload events
    case AudioTrack::EVENT_MARKER:
    case AudioTrack::EVENT_NEW_POS: {
        JNIEnv *env = AndroidRuntime::getJNIEnv();
        if (user != NULL && env != NULL) {
@@ -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},
+42 −11
Original line number Diff line number Diff line
@@ -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 {}

+15 −0
Original line number Diff line number Diff line
@@ -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
    /**
Loading