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

Commit f8726915 authored by Dichen Zhang's avatar Dichen Zhang
Browse files

Enable JAudioTrack.

MediaPlayer2 AudioOutput using public APIs.

Bug: 112549970
Test: android.media.cts.MediaPlayer2Test, android.media.cts.RoutingTest
Change-Id: Id76d80c040a52fd2ab724999697e222495906aec
parent c25db930
Loading
Loading
Loading
Loading
+1 −7
Original line number Diff line number Diff line
@@ -26,8 +26,7 @@ class JAudioAttributes {
public:
    /* Creates a Java AudioAttributes object. */
    static jobject createAudioAttributesObj(JNIEnv *env,
                                            const audio_attributes_t* pAttributes,
                                            audio_stream_type_t streamType) {
                                            const audio_attributes_t* pAttributes) {

        jclass jBuilderCls = env->FindClass("android/media/AudioAttributes$Builder");
        jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
@@ -58,11 +57,6 @@ public:
            // TODO: Handle the 'tags' (char[] to HashSet<String>).
            // How to parse the char[]? Is there any example of it?
            // Also, the addTags() method is hidden.
        } else {
            // Call AudioAttributes.Builder.setLegacyStreamType().build()
            jmethodID jSetLegacyStreamType = env->GetMethodID(jBuilderCls, "setLegacyStreamType",
                    "(I)Landroid/media/AudioAttributes$Builder;");
            jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetLegacyStreamType, streamType);
        }

        jmethodID jBuild = env->GetMethodID(jBuilderCls, "build",
+1 −0
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ cc_library_static {
        "libstagefright_player2",
        "libstagefright_rtsp",
        "libstagefright_timedtext2",
        "libmedia2_jni_core",
    ],

    export_include_dirs: [
+132 −23
Original line number Diff line number Diff line
@@ -28,7 +28,6 @@ namespace android {
// TODO: Store Java class/methodID as a member variable in the class.
// TODO: Add NULL && Exception checks after every JNI call.
JAudioTrack::JAudioTrack(                             // < Usages of the arguments are below >
        audio_stream_type_t streamType,               // AudioAudioAttributes
        uint32_t sampleRate,                          // AudioFormat && bufferSizeInBytes
        audio_format_t format,                        // AudioFormat && bufferSizeInBytes
        audio_channel_mask_t channelMask,             // AudioFormat && bufferSizeInBytes
@@ -40,8 +39,10 @@ JAudioTrack::JAudioTrack( // < Usages of the argumen
        float maxRequiredSpeed) {                     // bufferSizeInBytes

    JNIEnv *env = JavaVMHelper::getJNIEnv();

    jclass jAudioTrackCls = env->FindClass("android/media/AudioTrack");
    mAudioTrackCls = (jclass) env->NewGlobalRef(jAudioTrackCls);
    mAudioTrackCls = reinterpret_cast<jclass>(env->NewGlobalRef(jAudioTrackCls));
    env->DeleteLocalRef(jAudioTrackCls);

    maxRequiredSpeed = std::min(std::max(maxRequiredSpeed, 1.0f), AUDIO_TIMESTRETCH_SPEED_MAX);

@@ -64,10 +65,13 @@ JAudioTrack::JAudioTrack( // < Usages of the argumen
    jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
    jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor);

    jobject jAudioAttributesObj = JAudioAttributes::createAudioAttributesObj(env, pAttributes);
    mAudioAttributesObj = reinterpret_cast<jobject>(env->NewGlobalRef(jAudioAttributesObj));
    env->DeleteLocalRef(jAudioAttributesObj);

    jmethodID jSetAudioAttributes = env->GetMethodID(jBuilderCls, "setAudioAttributes",
            "(Landroid/media/AudioAttributes;)Landroid/media/AudioTrack$Builder;");
    jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetAudioAttributes,
            JAudioAttributes::createAudioAttributesObj(env, pAttributes, streamType));
    jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetAudioAttributes, mAudioAttributesObj);

    jmethodID jSetAudioFormat = env->GetMethodID(jBuilderCls, "setAudioFormat",
            "(Landroid/media/AudioFormat;)Landroid/media/AudioTrack$Builder;");
@@ -100,7 +104,9 @@ JAudioTrack::JAudioTrack( // < Usages of the argumen
    }

    jmethodID jBuild = env->GetMethodID(jBuilderCls, "build", "()Landroid/media/AudioTrack;");
    mAudioTrackObj = env->CallObjectMethod(jBuilderObj, jBuild);
    jobject jAudioTrackObj = env->CallObjectMethod(jBuilderObj, jBuild);
    mAudioTrackObj = reinterpret_cast<jobject>(env->NewGlobalRef(jAudioTrackObj));
    env->DeleteLocalRef(jBuilderObj);

    if (cbf != NULL) {
        // Set offload mode callback
@@ -118,6 +124,8 @@ JAudioTrack::JAudioTrack( // < Usages of the argumen
JAudioTrack::~JAudioTrack() {
    JNIEnv *env = JavaVMHelper::getJNIEnv();
    env->DeleteGlobalRef(mAudioTrackCls);
    env->DeleteGlobalRef(mAudioTrackObj);
    env->DeleteGlobalRef(mAudioAttributesObj);
}

size_t JAudioTrack::frameCount() {
@@ -151,21 +159,21 @@ status_t JAudioTrack::getPosition(uint32_t *position) {
    return NO_ERROR;
}

bool JAudioTrack::getTimestamp(AudioTimestamp& timestamp) {
status_t JAudioTrack::getTimestamp(AudioTimestamp& timestamp) {
    JNIEnv *env = JavaVMHelper::getJNIEnv();

    jclass jAudioTimeStampCls = env->FindClass("android/media/AudioTimestamp");
    jobject jAudioTimeStampObj = env->AllocObject(jAudioTimeStampCls);

    jfieldID jFramePosition = env->GetFieldID(jAudioTimeStampCls, "framePosition", "L");
    jfieldID jNanoTime = env->GetFieldID(jAudioTimeStampCls, "nanoTime", "L");
    jfieldID jFramePosition = env->GetFieldID(jAudioTimeStampCls, "framePosition", "J");
    jfieldID jNanoTime = env->GetFieldID(jAudioTimeStampCls, "nanoTime", "J");

    jmethodID jGetTimestamp = env->GetMethodID(mAudioTrackCls,
            "getTimestamp", "(Landroid/media/AudioTimestamp)B");
            "getTimestamp", "(Landroid/media/AudioTimestamp;)Z");
    bool success = env->CallBooleanMethod(mAudioTrackObj, jGetTimestamp, jAudioTimeStampObj);

    if (!success) {
        return false;
        return NO_INIT;
    }

    long long framePosition = env->GetLongField(jAudioTimeStampObj, jFramePosition);
@@ -178,7 +186,7 @@ bool JAudioTrack::getTimestamp(AudioTimestamp& timestamp) {
    timestamp.mTime = ts;
    timestamp.mPosition = (uint32_t) framePosition;

    return true;
    return NO_ERROR;
}

status_t JAudioTrack::getTimestamp(ExtendedTimestamp *timestamp __unused) {
@@ -423,6 +431,35 @@ audio_format_t JAudioTrack::format() {
    return audioFormatToNative(javaFormat);
}

size_t JAudioTrack::frameSize() {
    JNIEnv *env = JavaVMHelper::getJNIEnv();

    // TODO: Calculated here implementing the logic in AudioTrack.java
    // wait for AudioTrack.java exposing this parameter (i.e. getFrameSizeInBtytes())
    jmethodID jGetAudioFormat = env->GetMethodID(mAudioTrackCls, "getAudioFormat", "()I");
    int javaFormat = env->CallIntMethod(mAudioTrackObj, jGetAudioFormat);

    jclass jAudioFormatCls = env->FindClass("android/media/AudioFormat");
    jmethodID jIsEncodingLinearFrames = env->GetStaticMethodID(
            jAudioFormatCls, "isEncodingLinearFrames", "(I)Z");
    jboolean javaIsEncodingLinearFrames = env->CallStaticBooleanMethod(
            jAudioFormatCls, jIsEncodingLinearFrames, javaFormat);

    if (javaIsEncodingLinearFrames == false) {
        return 1;
    }

    jmethodID jGetBytesPerSample = env->GetStaticMethodID(jAudioFormatCls,
            "getBytesPerSample", "(I)I");
    int javaBytesPerSample = env->CallStaticIntMethod(jAudioFormatCls,
            jGetBytesPerSample, javaFormat);

    jmethodID jGetChannelCount = env->GetMethodID(mAudioTrackCls, "getChannelCount", "()I");
    int javaChannelCount = env->CallIntMethod(mAudioTrackObj, jGetChannelCount);

    return javaChannelCount * javaBytesPerSample;
}

status_t JAudioTrack::dump(int fd, const Vector<String16>& args __unused) const
{
    String8 result;
@@ -432,10 +469,6 @@ status_t JAudioTrack::dump(int fd, const Vector<String16>& args __unused) const
    // TODO: Remove logs that includes unavailable information from below.
//    result.appendFormat("  status(%d), state(%d), session Id(%d), flags(%#x)\n",
//                        mStatus, mState, mSessionId, mFlags);
//    result.appendFormat("  stream type(%d), left - right volume(%f, %f)\n",
//                        (mStreamType == AUDIO_STREAM_DEFAULT) ?
//                                audio_attributes_to_stream_type(&mAttributes) : mStreamType,
//                        mVolume[AUDIO_INTERLEAVE_LEFT], mVolume[AUDIO_INTERLEAVE_RIGHT]);
//    result.appendFormat("  format(%#x), channel mask(%#x), channel count(%u)\n",
//                  format(), mChannelMask, channelCount());
//    result.appendFormat("  sample rate(%u), original sample rate(%u), speed(%f)\n",
@@ -462,7 +495,7 @@ audio_port_handle_t JAudioTrack::getRoutedDeviceId() {
        return AUDIO_PORT_HANDLE_NONE;
    }

    jclass jAudioDeviceInfoCls = env->FindClass("Landroid/media/AudioDeviceInfo");
    jclass jAudioDeviceInfoCls = env->FindClass("android/media/AudioDeviceInfo");
    jmethodID jGetId = env->GetMethodID(jAudioDeviceInfoCls, "getId", "()I");
    jint routedDeviceId = env->CallIntMethod(jAudioDeviceInfoObj, jGetId);
    return routedDeviceId;
@@ -478,13 +511,22 @@ audio_session_t JAudioTrack::getAudioSessionId() {
status_t JAudioTrack::setOutputDevice(audio_port_handle_t deviceId) {
    JNIEnv *env = JavaVMHelper::getJNIEnv();
    jclass jMP2ImplCls = env->FindClass("android/media/MediaPlayer2Impl");
    jmethodID jSetAudioOutputDeviceById = env->GetMethodID(
    jmethodID jSetAudioOutputDeviceById = env->GetStaticMethodID(
            jMP2ImplCls, "setAudioOutputDeviceById", "(Landroid/media/AudioTrack;I)Z");
    jboolean result = env->CallStaticBooleanMethod(
            jMP2ImplCls, jSetAudioOutputDeviceById, mAudioTrackObj, deviceId);
    return result == true ? NO_ERROR : BAD_VALUE;
}

audio_stream_type_t JAudioTrack::getAudioStreamType() {
    JNIEnv *env = JavaVMHelper::getJNIEnv();
    jclass jAudioAttributesCls = env->FindClass("android/media/AudioAttributes");
    jmethodID jGetVolumeControlStream = env->GetMethodID(jAudioAttributesCls,
                                        "getVolumeControlStream", "()I");
    int javaAudioStreamType = env->CallIntMethod(mAudioAttributesObj, jGetVolumeControlStream);
    return (audio_stream_type_t)javaAudioStreamType;
}

status_t JAudioTrack::pendingDuration(int32_t *msec) {
    if (msec == nullptr) {
        return BAD_VALUE;
@@ -526,18 +568,85 @@ status_t JAudioTrack::pendingDuration(int32_t *msec) {
    return NO_ERROR;
}

status_t JAudioTrack::addAudioDeviceCallback(
        const sp<AudioSystem::AudioDeviceCallback>& callback __unused) {
    // TODO: Implement this after appropriate Java AudioTrack method is available.
status_t JAudioTrack::addAudioDeviceCallback(jobject listener, jobject handler) {
    JNIEnv *env = JavaVMHelper::getJNIEnv();
    jmethodID jAddOnRoutingChangedListener = env->GetMethodID(mAudioTrackCls,
            "addOnRoutingChangedListener",
            "(Landroid/media/AudioRouting$OnRoutingChangedListener;Landroid/os/Handler;)V");
    env->CallVoidMethod(mAudioTrackObj, jAddOnRoutingChangedListener, listener, handler);
    return NO_ERROR;
}

status_t JAudioTrack::removeAudioDeviceCallback(
        const sp<AudioSystem::AudioDeviceCallback>& callback __unused) {
    // TODO: Implement this after appropriate Java AudioTrack method is available.
status_t JAudioTrack::removeAudioDeviceCallback(jobject listener) {
    JNIEnv *env = JavaVMHelper::getJNIEnv();
    jmethodID jRemoveOnRoutingChangedListener = env->GetMethodID(mAudioTrackCls,
            "removeOnRoutingChangedListener",
            "(Landroid/media/AudioRouting$OnRoutingChangedListener;)V");
    env->CallVoidMethod(mAudioTrackObj, jRemoveOnRoutingChangedListener, listener);
    return NO_ERROR;
}

void JAudioTrack::registerRoutingDelegates(
        std::vector<std::pair<jobject, jobject>>& routingDelegates) {
    for (std::vector<std::pair<jobject, jobject>>::iterator it = routingDelegates.begin();
            it != routingDelegates.end(); it++) {
        addAudioDeviceCallback(it->second, getHandler(it->second));
    }
}

/////////////////////////////////////////////////////////////
///                Static methods begin                   ///
/////////////////////////////////////////////////////////////
jobject JAudioTrack::getListener(const jobject routingDelegateObj) {
    JNIEnv *env = JavaVMHelper::getJNIEnv();
    jclass jRoutingDelegateCls = env->FindClass("android/media/RoutingDelegate");
    jmethodID jGetListener = env->GetMethodID(jRoutingDelegateCls,
            "getListener", "()Landroid/media/AudioRouting$OnRoutingChangedListener;");
    return env->CallObjectMethod(routingDelegateObj, jGetListener);
}

jobject JAudioTrack::getHandler(const jobject routingDelegateObj) {
    JNIEnv *env = JavaVMHelper::getJNIEnv();
    jclass jRoutingDelegateCls = env->FindClass("android/media/RoutingDelegate");
    jmethodID jGetHandler = env->GetMethodID(jRoutingDelegateCls,
        "getHandler", "()Landroid/os/Handler;");
    return env->CallObjectMethod(routingDelegateObj, jGetHandler);
}

jobject JAudioTrack::addGlobalRef(const jobject obj) {
    JNIEnv *env = JavaVMHelper::getJNIEnv();
    return reinterpret_cast<jobject>(env->NewGlobalRef(obj));
}

status_t JAudioTrack::removeGlobalRef(const jobject obj) {
    if (obj == NULL) {
        return BAD_VALUE;
    }
    JNIEnv *env = JavaVMHelper::getJNIEnv();
    env->DeleteGlobalRef(obj);
    return NO_ERROR;
}

jobject JAudioTrack::findByKey(std::vector<std::pair<jobject, jobject>>& mp, const jobject key) {
    JNIEnv *env = JavaVMHelper::getJNIEnv();
    for (std::vector<std::pair<jobject, jobject>>::iterator it = mp.begin(); it != mp.end(); it++) {
        if (env->IsSameObject(it->first, key)) {
            return it->second;
        }
    }
    return nullptr;
}

void JAudioTrack::eraseByKey(std::vector<std::pair<jobject, jobject>>& mp, const jobject key) {
    JNIEnv *env = JavaVMHelper::getJNIEnv();
    for (std::vector<std::pair<jobject, jobject>>::iterator it = mp.begin(); it != mp.end(); it++) {
        if (env->IsSameObject(it->first, key)) {
            mp.erase(it);
            return;
        }
    }
}

/////////////////////////////////////////////////////////////
///                Private method begins                  ///
/////////////////////////////////////////////////////////////
+174 −195

File changed.

Preview size limit exceeded, changes collapsed.

+69 −17
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
#ifndef ANDROID_JAUDIOTRACK_H
#define ANDROID_JAUDIOTRACK_H

#include <vector>
#include <utility>
#include <jni.h>
#include <media/AudioResamplerPublic.h>
#include <media/AudioSystem.h>
@@ -29,7 +31,7 @@

namespace android {

class JAudioTrack {
class JAudioTrack : public RefBase {
public:

    /* Events used by AudioTrack callback function (callback_t).
@@ -37,6 +39,8 @@ public:
     */
    enum event_type {
        EVENT_MORE_DATA = 0,        // Request to write more data to buffer.
        EVENT_UNDERRUN = 1,         // Buffer underrun occurred. This will not occur for
                                    // static tracks.
        EVENT_NEW_IAUDIOTRACK = 6,  // IAudioTrack was re-created, either due to re-routing and
                                    // voluntary invalidation by mediaserver, or mediaserver crash.
        EVENT_STREAM_END = 7,       // Sent after all the buffers queued in AF and HW are played
@@ -104,8 +108,7 @@ public:
     *
     * TODO: Revive removed arguments after offload mode is supported.
     */
    JAudioTrack(audio_stream_type_t streamType,
                uint32_t sampleRate,
    JAudioTrack(uint32_t sampleRate,
                audio_format_t format,
                audio_channel_mask_t channelMask,
                callback_t cbf,
@@ -158,10 +161,10 @@ public:
     * Caution: calling this method too often may be inefficient;
     * if you need a high resolution mapping between frame position and presentation time,
     * consider implementing that at application level, based on the low resolution timestamps.
     * Returns true if timestamp is valid.
     * The timestamp parameter is undefined on return, if false is returned.
     * Returns NO_ERROR if timestamp is valid.
     *         NO_INIT if finds error, and timestamp parameter will be undefined on return.
     */
    bool getTimestamp(AudioTimestamp& timestamp);
    status_t getTimestamp(AudioTimestamp& timestamp);

    // TODO: This doc is just copied from AudioTrack.h. Revise it after implemenation.
    /* Return the extended timestamp, with additional timebase info and improved drain behavior.
@@ -324,6 +327,8 @@ public:

    audio_format_t format();

    size_t frameSize();

    /*
     * Dumps the state of an audio track.
     * Not a general-purpose API; intended only for use by media player service to dump its tracks.
@@ -355,6 +360,11 @@ public:
    /* Returns the flags */
    audio_output_flags_t getFlags() const { return mFlags; }

    /* We don't keep stream type here,
     * instead, we keep attributes and call getVolumeControlStream() to get stream type
     */
    audio_stream_type_t getAudioStreamType();

    /* Obtain the pending duration in milliseconds for playback of pure PCM data remaining in
     * AudioTrack.
     *
@@ -369,33 +379,75 @@ public:
     * Replaces any previously installed callback.
     *
     * Parameters:
     *
     * callback: The callback interface
     * Listener: the listener to receive notification of rerouting events.
     * Handler: the handler to handler the rerouting events.
     *
     * Returns NO_ERROR if successful.
     *         INVALID_OPERATION if the same callback is already installed.
     *         NO_INIT or PREMISSION_DENIED if AudioFlinger service is not reachable
     *         BAD_VALUE if the callback is NULL
     *         (TODO) INVALID_OPERATION if the same callback is already installed.
     *         (TODO) NO_INIT or PREMISSION_DENIED if AudioFlinger service is not reachable
     *         (TODO) BAD_VALUE if the callback is NULL
     */
    status_t addAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
    status_t addAudioDeviceCallback(jobject listener, jobject rd);

    /* Removes an AudioDeviceCallback.
     *
     * Parameters:
     *
     * callback: The callback interface
     * Listener: the listener to receive notification of rerouting events.
     *
     * Returns NO_ERROR if successful.
     *         INVALID_OPERATION if the callback is not installed
     *         BAD_VALUE if the callback is NULL
     *         (TODO) INVALID_OPERATION if the callback is not installed
     *         (TODO) BAD_VALUE if the callback is NULL
     */
    status_t removeAudioDeviceCallback(jobject listener);

    /* Register all backed-up routing delegates.
     *
     * Parameters:
     * routingDelegates: backed-up routing delegates
     *
     */
    void registerRoutingDelegates(std::vector<std::pair<jobject, jobject>>& routingDelegates);

    /* get listener from RoutingDelegate object
     */
    static jobject getListener(const jobject routingDelegateObj);

    /* get handler from RoutingDelegate object
     */
    static jobject getHandler(const jobject routingDelegateObj);

    /* convert local reference to global reference.
     */
    static jobject addGlobalRef(const jobject obj);

    /* erase global reference.
     *
     * Returns NO_ERROR if succeeds
     *         BAD_VALUE if obj is NULL
     */
    static status_t removeGlobalRef(const jobject obj);

    /*
     * Parameters:
     * map and key
     *
     * Returns value if key is in the map
     *         nullptr if key is not in the map
     */
    static jobject findByKey(std::vector<std::pair<jobject, jobject>>& mp, const jobject key);

    /*
     * Parameters:
     * map and key
     */
    status_t removeAudioDeviceCallback(const sp<AudioSystem::AudioDeviceCallback>& callback);
    static void eraseByKey(std::vector<std::pair<jobject, jobject>>& mp, const jobject key);

private:
    audio_output_flags_t mFlags;

    jclass mAudioTrackCls;
    jobject mAudioTrackObj;
    jobject mAudioAttributesObj;

    /* Creates a Java VolumeShaper.Configuration object from VolumeShaper::Configuration */
    jobject createVolumeShaperConfigurationObj(
Loading