Home | History | Annotate | Download | only in libmediaplayer2
      1 /*
      2  * Copyright 2018 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "JAudioTrack"
     18 
     19 #include "media/JAudioAttributes.h"
     20 #include "media/JAudioFormat.h"
     21 #include "mediaplayer2/JAudioTrack.h"
     22 
     23 #include <android_media_AudioErrors.h>
     24 #include <mediaplayer2/JavaVMHelper.h>
     25 
     26 namespace android {
     27 
     28 // TODO: Store Java class/methodID as a member variable in the class.
     29 // TODO: Add NULL && Exception checks after every JNI call.
     30 JAudioTrack::JAudioTrack(                             // < Usages of the arguments are below >
     31         uint32_t sampleRate,                          // AudioFormat && bufferSizeInBytes
     32         audio_format_t format,                        // AudioFormat && bufferSizeInBytes
     33         audio_channel_mask_t channelMask,             // AudioFormat && bufferSizeInBytes
     34         callback_t cbf,                               // Offload
     35         void* user,                                   // Offload
     36         size_t frameCount,                            // bufferSizeInBytes
     37         int32_t sessionId,                            // AudioTrack
     38         const jobject attributes,                     // AudioAttributes
     39         float maxRequiredSpeed) {                     // bufferSizeInBytes
     40 
     41     JNIEnv *env = JavaVMHelper::getJNIEnv();
     42 
     43     jclass jAudioTrackCls = env->FindClass("android/media/AudioTrack");
     44     mAudioTrackCls = reinterpret_cast<jclass>(env->NewGlobalRef(jAudioTrackCls));
     45     env->DeleteLocalRef(jAudioTrackCls);
     46 
     47     maxRequiredSpeed = std::min(std::max(maxRequiredSpeed, 1.0f), AUDIO_TIMESTRETCH_SPEED_MAX);
     48 
     49     int bufferSizeInBytes = 0;
     50     if (sampleRate == 0 || frameCount > 0) {
     51         // Manually calculate buffer size.
     52         bufferSizeInBytes = audio_channel_count_from_out_mask(channelMask)
     53                 * audio_bytes_per_sample(format) * (frameCount > 0 ? frameCount : 1);
     54     } else if (sampleRate > 0) {
     55         // Call Java AudioTrack::getMinBufferSize().
     56         jmethodID jGetMinBufferSize =
     57                 env->GetStaticMethodID(mAudioTrackCls, "getMinBufferSize", "(III)I");
     58         bufferSizeInBytes = env->CallStaticIntMethod(mAudioTrackCls, jGetMinBufferSize,
     59                 sampleRate, outChannelMaskFromNative(channelMask), audioFormatFromNative(format));
     60     }
     61     bufferSizeInBytes = (int) (bufferSizeInBytes * maxRequiredSpeed);
     62 
     63     // Create a Java AudioTrack object through its Builder.
     64     jclass jBuilderCls = env->FindClass("android/media/AudioTrack$Builder");
     65     jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
     66     jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor);
     67 
     68     {
     69         sp<JObjectHolder> audioAttributesObj;
     70         if (attributes != NULL) {
     71             audioAttributesObj = new JObjectHolder(attributes);
     72         } else {
     73             audioAttributesObj = new JObjectHolder(
     74                     JAudioAttributes::createAudioAttributesObj(env, NULL));
     75         }
     76         jmethodID jSetAudioAttributes = env->GetMethodID(jBuilderCls, "setAudioAttributes",
     77                 "(Landroid/media/AudioAttributes;)Landroid/media/AudioTrack$Builder;");
     78         jBuilderObj = env->CallObjectMethod(jBuilderObj,
     79                 jSetAudioAttributes, audioAttributesObj->getJObject());
     80     }
     81 
     82     jmethodID jSetAudioFormat = env->GetMethodID(jBuilderCls, "setAudioFormat",
     83             "(Landroid/media/AudioFormat;)Landroid/media/AudioTrack$Builder;");
     84     jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetAudioFormat,
     85             JAudioFormat::createAudioFormatObj(env, sampleRate, format, channelMask));
     86 
     87     jmethodID jSetBufferSizeInBytes = env->GetMethodID(jBuilderCls, "setBufferSizeInBytes",
     88             "(I)Landroid/media/AudioTrack$Builder;");
     89     jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetBufferSizeInBytes, bufferSizeInBytes);
     90 
     91     // We only use streaming mode of Java AudioTrack.
     92     jfieldID jModeStream = env->GetStaticFieldID(mAudioTrackCls, "MODE_STREAM", "I");
     93     jint transferMode = env->GetStaticIntField(mAudioTrackCls, jModeStream);
     94     jmethodID jSetTransferMode = env->GetMethodID(jBuilderCls, "setTransferMode",
     95             "(I)Landroid/media/AudioTrack$Builder;");
     96     jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetTransferMode,
     97             transferMode /* Java AudioTrack::MODE_STREAM */);
     98 
     99     if (sessionId != 0) {
    100         jmethodID jSetSessionId = env->GetMethodID(jBuilderCls, "setSessionId",
    101                 "(I)Landroid/media/AudioTrack$Builder;");
    102         jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetSessionId, sessionId);
    103     }
    104 
    105     mFlags = AUDIO_OUTPUT_FLAG_NONE;
    106     if (cbf != NULL) {
    107         jmethodID jSetOffloadedPlayback = env->GetMethodID(jBuilderCls, "setOffloadedPlayback",
    108                 "(Z)Landroid/media/AudioTrack$Builder;");
    109         jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetOffloadedPlayback, true);
    110         mFlags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
    111     }
    112 
    113     jmethodID jBuild = env->GetMethodID(jBuilderCls, "build", "()Landroid/media/AudioTrack;");
    114     jobject jAudioTrackObj = env->CallObjectMethod(jBuilderObj, jBuild);
    115     mAudioTrackObj = reinterpret_cast<jobject>(env->NewGlobalRef(jAudioTrackObj));
    116     env->DeleteLocalRef(jBuilderObj);
    117 
    118     if (cbf != NULL) {
    119         // Set offload mode callback
    120         jobject jStreamEventCallbackObj = createStreamEventCallback(cbf, user);
    121         jobject jExecutorObj = createCallbackExecutor();
    122         jmethodID jSetStreamEventCallback = env->GetMethodID(
    123                 jAudioTrackCls,
    124                 "setStreamEventCallback",
    125                 "(Ljava/util/concurrent/Executor;Landroid/media/AudioTrack$StreamEventCallback;)V");
    126         env->CallVoidMethod(
    127                 mAudioTrackObj, jSetStreamEventCallback, jExecutorObj, jStreamEventCallbackObj);
    128     }
    129 }
    130 
    131 JAudioTrack::~JAudioTrack() {
    132     JNIEnv *env = JavaVMHelper::getJNIEnv();
    133     env->DeleteGlobalRef(mAudioTrackCls);
    134     env->DeleteGlobalRef(mAudioTrackObj);
    135 }
    136 
    137 size_t JAudioTrack::frameCount() {
    138     JNIEnv *env = JavaVMHelper::getJNIEnv();
    139     jmethodID jGetBufferSizeInFrames = env->GetMethodID(
    140             mAudioTrackCls, "getBufferSizeInFrames", "()I");
    141     return env->CallIntMethod(mAudioTrackObj, jGetBufferSizeInFrames);
    142 }
    143 
    144 size_t JAudioTrack::channelCount() {
    145     JNIEnv *env = JavaVMHelper::getJNIEnv();
    146     jmethodID jGetChannelCount = env->GetMethodID(mAudioTrackCls, "getChannelCount", "()I");
    147     return env->CallIntMethod(mAudioTrackObj, jGetChannelCount);
    148 }
    149 
    150 uint32_t JAudioTrack::latency() {
    151     // TODO: Currently hard-coded as returning zero.
    152     return 0;
    153 }
    154 
    155 status_t JAudioTrack::getPosition(uint32_t *position) {
    156     if (position == NULL) {
    157         return BAD_VALUE;
    158     }
    159 
    160     JNIEnv *env = JavaVMHelper::getJNIEnv();
    161     jmethodID jGetPlaybackHeadPosition = env->GetMethodID(
    162             mAudioTrackCls, "getPlaybackHeadPosition", "()I");
    163     *position = env->CallIntMethod(mAudioTrackObj, jGetPlaybackHeadPosition);
    164 
    165     return NO_ERROR;
    166 }
    167 
    168 status_t JAudioTrack::getTimestamp(AudioTimestamp& timestamp) {
    169     JNIEnv *env = JavaVMHelper::getJNIEnv();
    170 
    171     jclass jAudioTimeStampCls = env->FindClass("android/media/AudioTimestamp");
    172     jobject jAudioTimeStampObj = env->AllocObject(jAudioTimeStampCls);
    173 
    174     jfieldID jFramePosition = env->GetFieldID(jAudioTimeStampCls, "framePosition", "J");
    175     jfieldID jNanoTime = env->GetFieldID(jAudioTimeStampCls, "nanoTime", "J");
    176 
    177     jmethodID jGetTimestamp = env->GetMethodID(mAudioTrackCls,
    178             "getTimestamp", "(Landroid/media/AudioTimestamp;)Z");
    179     bool success = env->CallBooleanMethod(mAudioTrackObj, jGetTimestamp, jAudioTimeStampObj);
    180 
    181     if (!success) {
    182         return NO_INIT;
    183     }
    184 
    185     long long framePosition = env->GetLongField(jAudioTimeStampObj, jFramePosition);
    186     long long nanoTime = env->GetLongField(jAudioTimeStampObj, jNanoTime);
    187 
    188     struct timespec ts;
    189     const long long secondToNano = 1000000000LL; // 1E9
    190     ts.tv_sec = nanoTime / secondToNano;
    191     ts.tv_nsec = nanoTime % secondToNano;
    192     timestamp.mTime = ts;
    193     timestamp.mPosition = (uint32_t) framePosition;
    194 
    195     return NO_ERROR;
    196 }
    197 
    198 status_t JAudioTrack::getTimestamp(ExtendedTimestamp *timestamp __unused) {
    199     // TODO: Implement this after appropriate Java AudioTrack method is available.
    200     return NO_ERROR;
    201 }
    202 
    203 status_t JAudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate) {
    204     // TODO: existing native AudioTrack returns INVALID_OPERATION on offload/direct/fast tracks.
    205     // Should we do the same thing?
    206     JNIEnv *env = JavaVMHelper::getJNIEnv();
    207 
    208     jclass jPlaybackParamsCls = env->FindClass("android/media/PlaybackParams");
    209     jmethodID jPlaybackParamsCtor = env->GetMethodID(jPlaybackParamsCls, "<init>", "()V");
    210     jobject jPlaybackParamsObj = env->NewObject(jPlaybackParamsCls, jPlaybackParamsCtor);
    211 
    212     jmethodID jSetAudioFallbackMode = env->GetMethodID(
    213             jPlaybackParamsCls, "setAudioFallbackMode", "(I)Landroid/media/PlaybackParams;");
    214     jPlaybackParamsObj = env->CallObjectMethod(
    215             jPlaybackParamsObj, jSetAudioFallbackMode, playbackRate.mFallbackMode);
    216 
    217     jmethodID jSetAudioStretchMode = env->GetMethodID(
    218                 jPlaybackParamsCls, "setAudioStretchMode", "(I)Landroid/media/PlaybackParams;");
    219     jPlaybackParamsObj = env->CallObjectMethod(
    220             jPlaybackParamsObj, jSetAudioStretchMode, playbackRate.mStretchMode);
    221 
    222     jmethodID jSetPitch = env->GetMethodID(
    223             jPlaybackParamsCls, "setPitch", "(F)Landroid/media/PlaybackParams;");
    224     jPlaybackParamsObj = env->CallObjectMethod(jPlaybackParamsObj, jSetPitch, playbackRate.mPitch);
    225 
    226     jmethodID jSetSpeed = env->GetMethodID(
    227             jPlaybackParamsCls, "setSpeed", "(F)Landroid/media/PlaybackParams;");
    228     jPlaybackParamsObj = env->CallObjectMethod(jPlaybackParamsObj, jSetSpeed, playbackRate.mSpeed);
    229 
    230 
    231     // Set this Java PlaybackParams object into Java AudioTrack.
    232     jmethodID jSetPlaybackParams = env->GetMethodID(
    233             mAudioTrackCls, "setPlaybackParams", "(Landroid/media/PlaybackParams;)V");
    234     env->CallVoidMethod(mAudioTrackObj, jSetPlaybackParams, jPlaybackParamsObj);
    235     // TODO: Should we catch the Java IllegalArgumentException?
    236 
    237     return NO_ERROR;
    238 }
    239 
    240 const AudioPlaybackRate JAudioTrack::getPlaybackRate() {
    241     JNIEnv *env = JavaVMHelper::getJNIEnv();
    242 
    243     jmethodID jGetPlaybackParams = env->GetMethodID(
    244             mAudioTrackCls, "getPlaybackParams", "()Landroid/media/PlaybackParams;");
    245     jobject jPlaybackParamsObj = env->CallObjectMethod(mAudioTrackObj, jGetPlaybackParams);
    246 
    247     AudioPlaybackRate playbackRate;
    248     jclass jPlaybackParamsCls = env->FindClass("android/media/PlaybackParams");
    249 
    250     jmethodID jGetAudioFallbackMode = env->GetMethodID(
    251             jPlaybackParamsCls, "getAudioFallbackMode", "()I");
    252     // TODO: Should we enable passing AUDIO_TIMESTRETCH_FALLBACK_CUT_REPEAT?
    253     //       The enum is internal only, so it is not defined in PlaybackParmas.java.
    254     // TODO: Is this right way to convert an int to an enum?
    255     playbackRate.mFallbackMode = static_cast<AudioTimestretchFallbackMode>(
    256             env->CallIntMethod(jPlaybackParamsObj, jGetAudioFallbackMode));
    257 
    258     jmethodID jGetAudioStretchMode = env->GetMethodID(
    259             jPlaybackParamsCls, "getAudioStretchMode", "()I");
    260     playbackRate.mStretchMode = static_cast<AudioTimestretchStretchMode>(
    261             env->CallIntMethod(jPlaybackParamsObj, jGetAudioStretchMode));
    262 
    263     jmethodID jGetPitch = env->GetMethodID(jPlaybackParamsCls, "getPitch", "()F");
    264     playbackRate.mPitch = env->CallFloatMethod(jPlaybackParamsObj, jGetPitch);
    265 
    266     jmethodID jGetSpeed = env->GetMethodID(jPlaybackParamsCls, "getSpeed", "()F");
    267     playbackRate.mSpeed = env->CallFloatMethod(jPlaybackParamsObj, jGetSpeed);
    268 
    269     return playbackRate;
    270 }
    271 
    272 media::VolumeShaper::Status JAudioTrack::applyVolumeShaper(
    273         const sp<media::VolumeShaper::Configuration>& configuration,
    274         const sp<media::VolumeShaper::Operation>& operation) {
    275 
    276     jobject jConfigurationObj = createVolumeShaperConfigurationObj(configuration);
    277     jobject jOperationObj = createVolumeShaperOperationObj(operation);
    278 
    279     if (jConfigurationObj == NULL || jOperationObj == NULL) {
    280         return media::VolumeShaper::Status(BAD_VALUE);
    281     }
    282 
    283     JNIEnv *env = JavaVMHelper::getJNIEnv();
    284 
    285     jmethodID jCreateVolumeShaper = env->GetMethodID(mAudioTrackCls, "createVolumeShaper",
    286             "(Landroid/media/VolumeShaper$Configuration;)Landroid/media/VolumeShaper;");
    287     jobject jVolumeShaperObj = env->CallObjectMethod(
    288             mAudioTrackObj, jCreateVolumeShaper, jConfigurationObj);
    289 
    290     jclass jVolumeShaperCls = env->FindClass("android/media/VolumeShaper");
    291     jmethodID jApply = env->GetMethodID(jVolumeShaperCls, "apply",
    292             "(Landroid/media/VolumeShaper$Operation;)V");
    293     env->CallVoidMethod(jVolumeShaperObj, jApply, jOperationObj);
    294 
    295     return media::VolumeShaper::Status(NO_ERROR);
    296 }
    297 
    298 status_t JAudioTrack::setAuxEffectSendLevel(float level) {
    299     JNIEnv *env = JavaVMHelper::getJNIEnv();
    300     jmethodID jSetAuxEffectSendLevel = env->GetMethodID(
    301             mAudioTrackCls, "setAuxEffectSendLevel", "(F)I");
    302     int result = env->CallIntMethod(mAudioTrackObj, jSetAuxEffectSendLevel, level);
    303     return javaToNativeStatus(result);
    304 }
    305 
    306 status_t JAudioTrack::attachAuxEffect(int effectId) {
    307     JNIEnv *env = JavaVMHelper::getJNIEnv();
    308     jmethodID jAttachAuxEffect = env->GetMethodID(mAudioTrackCls, "attachAuxEffect", "(I)I");
    309     int result = env->CallIntMethod(mAudioTrackObj, jAttachAuxEffect, effectId);
    310     return javaToNativeStatus(result);
    311 }
    312 
    313 status_t JAudioTrack::setVolume(float left, float right) {
    314     JNIEnv *env = JavaVMHelper::getJNIEnv();
    315     // TODO: Java setStereoVolume is deprecated. Do we really need this method?
    316     jmethodID jSetStereoVolume = env->GetMethodID(mAudioTrackCls, "setStereoVolume", "(FF)I");
    317     int result = env->CallIntMethod(mAudioTrackObj, jSetStereoVolume, left, right);
    318     return javaToNativeStatus(result);
    319 }
    320 
    321 status_t JAudioTrack::setVolume(float volume) {
    322     JNIEnv *env = JavaVMHelper::getJNIEnv();
    323     jmethodID jSetVolume = env->GetMethodID(mAudioTrackCls, "setVolume", "(F)I");
    324     int result = env->CallIntMethod(mAudioTrackObj, jSetVolume, volume);
    325     return javaToNativeStatus(result);
    326 }
    327 
    328 status_t JAudioTrack::start() {
    329     JNIEnv *env = JavaVMHelper::getJNIEnv();
    330     jmethodID jPlay = env->GetMethodID(mAudioTrackCls, "play", "()V");
    331     // TODO: Should we catch the Java IllegalStateException from play()?
    332     env->CallVoidMethod(mAudioTrackObj, jPlay);
    333     return NO_ERROR;
    334 }
    335 
    336 ssize_t JAudioTrack::write(const void* buffer, size_t size, bool blocking) {
    337     if (buffer == NULL) {
    338         return BAD_VALUE;
    339     }
    340 
    341     JNIEnv *env = JavaVMHelper::getJNIEnv();
    342     jbyteArray jAudioData = env->NewByteArray(size);
    343     env->SetByteArrayRegion(jAudioData, 0, size, (jbyte *) buffer);
    344 
    345     jclass jByteBufferCls = env->FindClass("java/nio/ByteBuffer");
    346     jmethodID jWrap = env->GetStaticMethodID(jByteBufferCls, "wrap", "([B)Ljava/nio/ByteBuffer;");
    347     jobject jByteBufferObj = env->CallStaticObjectMethod(jByteBufferCls, jWrap, jAudioData);
    348 
    349     int writeMode = 0;
    350     if (blocking) {
    351         jfieldID jWriteBlocking = env->GetStaticFieldID(mAudioTrackCls, "WRITE_BLOCKING", "I");
    352         writeMode = env->GetStaticIntField(mAudioTrackCls, jWriteBlocking);
    353     } else {
    354         jfieldID jWriteNonBlocking = env->GetStaticFieldID(
    355                 mAudioTrackCls, "WRITE_NON_BLOCKING", "I");
    356         writeMode = env->GetStaticIntField(mAudioTrackCls, jWriteNonBlocking);
    357     }
    358 
    359     jmethodID jWrite = env->GetMethodID(mAudioTrackCls, "write", "(Ljava/nio/ByteBuffer;II)I");
    360     int result = env->CallIntMethod(mAudioTrackObj, jWrite, jByteBufferObj, size, writeMode);
    361 
    362     if (result >= 0) {
    363         return result;
    364     } else {
    365         return javaToNativeStatus(result);
    366     }
    367 }
    368 
    369 void JAudioTrack::stop() {
    370     JNIEnv *env = JavaVMHelper::getJNIEnv();
    371     jmethodID jStop = env->GetMethodID(mAudioTrackCls, "stop", "()V");
    372     env->CallVoidMethod(mAudioTrackObj, jStop);
    373     // TODO: Should we catch IllegalStateException?
    374 }
    375 
    376 // TODO: Is the right implementation?
    377 bool JAudioTrack::stopped() const {
    378     return !isPlaying();
    379 }
    380 
    381 void JAudioTrack::flush() {
    382     JNIEnv *env = JavaVMHelper::getJNIEnv();
    383     jmethodID jFlush = env->GetMethodID(mAudioTrackCls, "flush", "()V");
    384     env->CallVoidMethod(mAudioTrackObj, jFlush);
    385 }
    386 
    387 void JAudioTrack::pause() {
    388     JNIEnv *env = JavaVMHelper::getJNIEnv();
    389     jmethodID jPause = env->GetMethodID(mAudioTrackCls, "pause", "()V");
    390     env->CallVoidMethod(mAudioTrackObj, jPause);
    391     // TODO: Should we catch IllegalStateException?
    392 }
    393 
    394 bool JAudioTrack::isPlaying() const {
    395     JNIEnv *env = JavaVMHelper::getJNIEnv();
    396     jmethodID jGetPlayState = env->GetMethodID(mAudioTrackCls, "getPlayState", "()I");
    397     int currentPlayState = env->CallIntMethod(mAudioTrackObj, jGetPlayState);
    398 
    399     // TODO: In Java AudioTrack, there is no STOPPING state.
    400     // This means while stopping, isPlaying() will return different value in two class.
    401     //  - in existing native AudioTrack: true
    402     //  - in JAudioTrack: false
    403     // If not okay, also modify the implementation of stopped().
    404     jfieldID jPlayStatePlaying = env->GetStaticFieldID(mAudioTrackCls, "PLAYSTATE_PLAYING", "I");
    405     int statePlaying = env->GetStaticIntField(mAudioTrackCls, jPlayStatePlaying);
    406     return currentPlayState == statePlaying;
    407 }
    408 
    409 uint32_t JAudioTrack::getSampleRate() {
    410     JNIEnv *env = JavaVMHelper::getJNIEnv();
    411     jmethodID jGetSampleRate = env->GetMethodID(mAudioTrackCls, "getSampleRate", "()I");
    412     return env->CallIntMethod(mAudioTrackObj, jGetSampleRate);
    413 }
    414 
    415 status_t JAudioTrack::getBufferDurationInUs(int64_t *duration) {
    416     if (duration == nullptr) {
    417         return BAD_VALUE;
    418     }
    419 
    420     JNIEnv *env = JavaVMHelper::getJNIEnv();
    421     jmethodID jGetBufferSizeInFrames = env->GetMethodID(
    422             mAudioTrackCls, "getBufferSizeInFrames", "()I");
    423     int bufferSizeInFrames = env->CallIntMethod(mAudioTrackObj, jGetBufferSizeInFrames);
    424 
    425     const double secondToMicro = 1000000LL; // 1E6
    426     int sampleRate = JAudioTrack::getSampleRate();
    427     float speed = JAudioTrack::getPlaybackRate().mSpeed;
    428 
    429     *duration = (int64_t) (bufferSizeInFrames * secondToMicro / (sampleRate * speed));
    430     return NO_ERROR;
    431 }
    432 
    433 audio_format_t JAudioTrack::format() {
    434     JNIEnv *env = JavaVMHelper::getJNIEnv();
    435     jmethodID jGetAudioFormat = env->GetMethodID(mAudioTrackCls, "getAudioFormat", "()I");
    436     int javaFormat = env->CallIntMethod(mAudioTrackObj, jGetAudioFormat);
    437     return audioFormatToNative(javaFormat);
    438 }
    439 
    440 size_t JAudioTrack::frameSize() {
    441     JNIEnv *env = JavaVMHelper::getJNIEnv();
    442     jmethodID jGetFormat = env->GetMethodID(mAudioTrackCls,
    443             "getFormat", "()Landroid/media/AudioFormat;");
    444     jobject jAudioFormatObj = env->CallObjectMethod(mAudioTrackObj, jGetFormat);
    445 
    446     jclass jAudioFormatCls = env->FindClass("android/media/AudioFormat");
    447     jmethodID jGetFrameSizeInBytes = env->GetMethodID(
    448             jAudioFormatCls, "getFrameSizeInBytes", "()I");
    449     jint javaFrameSizeInBytes = env->CallIntMethod(jAudioFormatObj, jGetFrameSizeInBytes);
    450 
    451     return (size_t)javaFrameSizeInBytes;
    452 }
    453 
    454 status_t JAudioTrack::dump(int fd, const Vector<String16>& args __unused) const
    455 {
    456     String8 result;
    457 
    458     result.append(" JAudioTrack::dump\n");
    459 
    460     // TODO: Remove logs that includes unavailable information from below.
    461 //    result.appendFormat("  status(%d), state(%d), session Id(%d), flags(%#x)\n",
    462 //                        mStatus, mState, mSessionId, mFlags);
    463 //    result.appendFormat("  format(%#x), channel mask(%#x), channel count(%u)\n",
    464 //                  format(), mChannelMask, channelCount());
    465 //    result.appendFormat("  sample rate(%u), original sample rate(%u), speed(%f)\n",
    466 //            getSampleRate(), mOriginalSampleRate, mPlaybackRate.mSpeed);
    467 //    result.appendFormat("  frame count(%zu), req. frame count(%zu)\n",
    468 //                  frameCount(), mReqFrameCount);
    469 //    result.appendFormat("  notif. frame count(%u), req. notif. frame count(%u),"
    470 //            " req. notif. per buff(%u)\n",
    471 //             mNotificationFramesAct, mNotificationFramesReq, mNotificationsPerBufferReq);
    472 //    result.appendFormat("  latency (%d), selected device Id(%d), routed device Id(%d)\n",
    473 //                        latency(), mSelectedDeviceId, getRoutedDeviceId());
    474 //    result.appendFormat("  output(%d) AF latency (%u) AF frame count(%zu) AF SampleRate(%u)\n",
    475 //                        mOutput, mAfLatency, mAfFrameCount, mAfSampleRate);
    476     ::write(fd, result.string(), result.size());
    477     return NO_ERROR;
    478 }
    479 
    480 jobject JAudioTrack::getRoutedDevice() {
    481     JNIEnv *env = JavaVMHelper::getJNIEnv();
    482     jmethodID jGetRoutedDevice = env->GetMethodID(mAudioTrackCls, "getRoutedDevice",
    483             "()Landroid/media/AudioDeviceInfo;");
    484     return env->CallObjectMethod(mAudioTrackObj, jGetRoutedDevice);
    485 }
    486 
    487 int32_t JAudioTrack::getAudioSessionId() {
    488     JNIEnv *env = JavaVMHelper::getJNIEnv();
    489     jmethodID jGetAudioSessionId = env->GetMethodID(mAudioTrackCls, "getAudioSessionId", "()I");
    490     jint sessionId = env->CallIntMethod(mAudioTrackObj, jGetAudioSessionId);
    491     return sessionId;
    492 }
    493 
    494 status_t JAudioTrack::setPreferredDevice(jobject device) {
    495     JNIEnv *env = JavaVMHelper::getJNIEnv();
    496     jmethodID jSetPreferredDeviceId = env->GetMethodID(mAudioTrackCls, "setPreferredDevice",
    497             "(Landroid/media/AudioDeviceInfo;)Z");
    498     jboolean result = env->CallBooleanMethod(mAudioTrackObj, jSetPreferredDeviceId, device);
    499     return result == true ? NO_ERROR : BAD_VALUE;
    500 }
    501 
    502 audio_stream_type_t JAudioTrack::getAudioStreamType() {
    503     JNIEnv *env = JavaVMHelper::getJNIEnv();
    504     jmethodID jGetAudioAttributes = env->GetMethodID(mAudioTrackCls, "getAudioAttributes",
    505             "()Landroid/media/AudioAttributes;");
    506     jobject jAudioAttributes = env->CallObjectMethod(mAudioTrackObj, jGetAudioAttributes);
    507     jclass jAudioAttributesCls = env->FindClass("android/media/AudioAttributes");
    508     jmethodID jGetVolumeControlStream = env->GetMethodID(jAudioAttributesCls,
    509             "getVolumeControlStream", "()I");
    510     int javaAudioStreamType = env->CallIntMethod(jAudioAttributes, jGetVolumeControlStream);
    511     return (audio_stream_type_t)javaAudioStreamType;
    512 }
    513 
    514 status_t JAudioTrack::pendingDuration(int32_t *msec) {
    515     if (msec == nullptr) {
    516         return BAD_VALUE;
    517     }
    518 
    519     bool isPurePcmData = audio_is_linear_pcm(format()) && (getFlags() & AUDIO_FLAG_HW_AV_SYNC) == 0;
    520     if (!isPurePcmData) {
    521         return INVALID_OPERATION;
    522     }
    523 
    524     // TODO: Need to know the difference btw. client and server time.
    525     // If getTimestamp(ExtendedTimestamp) is ready, and un-comment below and modify appropriately.
    526     // (copied from AudioTrack.cpp)
    527 
    528 //    ExtendedTimestamp ets;
    529 //    ExtendedTimestamp::LOCATION location = ExtendedTimestamp::LOCATION_SERVER;
    530 //    if (getTimestamp_l(&ets) == OK && ets.mTimeNs[location] > 0) {
    531 //        int64_t diff = ets.mPosition[ExtendedTimestamp::LOCATION_CLIENT]
    532 //                - ets.mPosition[location];
    533 //        if (diff < 0) {
    534 //            *msec = 0;
    535 //        } else {
    536 //            // ms is the playback time by frames
    537 //            int64_t ms = (int64_t)((double)diff * 1000 /
    538 //                    ((double)mSampleRate * mPlaybackRate.mSpeed));
    539 //            // clockdiff is the timestamp age (negative)
    540 //            int64_t clockdiff = (mState != STATE_ACTIVE) ? 0 :
    541 //                    ets.mTimeNs[location]
    542 //                    + ets.mTimebaseOffset[ExtendedTimestamp::TIMEBASE_MONOTONIC]
    543 //                    - systemTime(SYSTEM_TIME_MONOTONIC);
    544 //
    545 //            //ALOGV("ms: %lld  clockdiff: %lld", (long long)ms, (long long)clockdiff);
    546 //            static const int NANOS_PER_MILLIS = 1000000;
    547 //            *msec = (int32_t)(ms + clockdiff / NANOS_PER_MILLIS);
    548 //        }
    549 //        return NO_ERROR;
    550 //    }
    551 
    552     return NO_ERROR;
    553 }
    554 
    555 status_t JAudioTrack::addAudioDeviceCallback(jobject listener, jobject handler) {
    556     JNIEnv *env = JavaVMHelper::getJNIEnv();
    557     jmethodID jAddOnRoutingChangedListener = env->GetMethodID(mAudioTrackCls,
    558             "addOnRoutingChangedListener",
    559             "(Landroid/media/AudioRouting$OnRoutingChangedListener;Landroid/os/Handler;)V");
    560     env->CallVoidMethod(mAudioTrackObj, jAddOnRoutingChangedListener, listener, handler);
    561     return NO_ERROR;
    562 }
    563 
    564 status_t JAudioTrack::removeAudioDeviceCallback(jobject listener) {
    565     JNIEnv *env = JavaVMHelper::getJNIEnv();
    566     jmethodID jRemoveOnRoutingChangedListener = env->GetMethodID(mAudioTrackCls,
    567             "removeOnRoutingChangedListener",
    568             "(Landroid/media/AudioRouting$OnRoutingChangedListener;)V");
    569     env->CallVoidMethod(mAudioTrackObj, jRemoveOnRoutingChangedListener, listener);
    570     return NO_ERROR;
    571 }
    572 
    573 void JAudioTrack::registerRoutingDelegates(
    574         Vector<std::pair<sp<JObjectHolder>, sp<JObjectHolder>>>& routingDelegates) {
    575     for (auto it = routingDelegates.begin(); it != routingDelegates.end(); it++) {
    576         addAudioDeviceCallback(it->second->getJObject(), getHandler(it->second->getJObject()));
    577     }
    578 }
    579 
    580 /////////////////////////////////////////////////////////////
    581 ///                Static methods begin                   ///
    582 /////////////////////////////////////////////////////////////
    583 jobject JAudioTrack::getListener(const jobject routingDelegateObj) {
    584     JNIEnv *env = JavaVMHelper::getJNIEnv();
    585     jclass jRoutingDelegateCls = env->FindClass("android/media/RoutingDelegate");
    586     jmethodID jGetListener = env->GetMethodID(jRoutingDelegateCls,
    587             "getListener", "()Landroid/media/AudioRouting$OnRoutingChangedListener;");
    588     return env->CallObjectMethod(routingDelegateObj, jGetListener);
    589 }
    590 
    591 jobject JAudioTrack::getHandler(const jobject routingDelegateObj) {
    592     JNIEnv *env = JavaVMHelper::getJNIEnv();
    593     jclass jRoutingDelegateCls = env->FindClass("android/media/RoutingDelegate");
    594     jmethodID jGetHandler = env->GetMethodID(jRoutingDelegateCls,
    595         "getHandler", "()Landroid/os/Handler;");
    596     return env->CallObjectMethod(routingDelegateObj, jGetHandler);
    597 }
    598 
    599 jobject JAudioTrack::findByKey(
    600         Vector<std::pair<sp<JObjectHolder>, sp<JObjectHolder>>>& mp, const jobject key) {
    601     JNIEnv *env = JavaVMHelper::getJNIEnv();
    602     for (auto it = mp.begin(); it != mp.end(); it++) {
    603         if (env->IsSameObject(it->first->getJObject(), key)) {
    604             return it->second->getJObject();
    605         }
    606     }
    607     return nullptr;
    608 }
    609 
    610 void JAudioTrack::eraseByKey(
    611         Vector<std::pair<sp<JObjectHolder>, sp<JObjectHolder>>>& mp, const jobject key) {
    612     JNIEnv *env = JavaVMHelper::getJNIEnv();
    613     for (auto it = mp.begin(); it != mp.end(); it++) {
    614         if (env->IsSameObject(it->first->getJObject(), key)) {
    615             mp.erase(it);
    616             return;
    617         }
    618     }
    619 }
    620 
    621 /////////////////////////////////////////////////////////////
    622 ///                Private method begins                  ///
    623 /////////////////////////////////////////////////////////////
    624 
    625 jobject JAudioTrack::createVolumeShaperConfigurationObj(
    626         const sp<media::VolumeShaper::Configuration>& config) {
    627 
    628     // TODO: Java VolumeShaper's setId() / setOptionFlags() are hidden.
    629     if (config == NULL || config->getType() == media::VolumeShaper::Configuration::TYPE_ID) {
    630         return NULL;
    631     }
    632 
    633     JNIEnv *env = JavaVMHelper::getJNIEnv();
    634 
    635     // Referenced "android_media_VolumeShaper.h".
    636     jfloatArray xarray = nullptr;
    637     jfloatArray yarray = nullptr;
    638     if (config->getType() == media::VolumeShaper::Configuration::TYPE_SCALE) {
    639         // convert curve arrays
    640         xarray = env->NewFloatArray(config->size());
    641         yarray = env->NewFloatArray(config->size());
    642         float * const x = env->GetFloatArrayElements(xarray, nullptr /* isCopy */);
    643         float * const y = env->GetFloatArrayElements(yarray, nullptr /* isCopy */);
    644         float *xptr = x, *yptr = y;
    645         for (const auto &pt : *config.get()) {
    646             *xptr++ = pt.first;
    647             *yptr++ = pt.second;
    648         }
    649         env->ReleaseFloatArrayElements(xarray, x, 0 /* mode */);
    650         env->ReleaseFloatArrayElements(yarray, y, 0 /* mode */);
    651     }
    652 
    653     jclass jBuilderCls = env->FindClass("android/media/VolumeShaper$Configuration$Builder");
    654     jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
    655     jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor);
    656 
    657     jmethodID jSetDuration = env->GetMethodID(jBuilderCls, "setDuration",
    658             "(L)Landroid/media/VolumeShaper$Configuration$Builder;");
    659     jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetDuration, (jlong) config->getDurationMs());
    660 
    661     jmethodID jSetInterpolatorType = env->GetMethodID(jBuilderCls, "setInterpolatorType",
    662             "(I)Landroid/media/VolumeShaper$Configuration$Builder;");
    663     jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetInterpolatorType,
    664             config->getInterpolatorType());
    665 
    666     jmethodID jSetCurve = env->GetMethodID(jBuilderCls, "setCurve",
    667             "([F[F)Landroid/media/VolumeShaper$Configuration$Builder;");
    668     jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetCurve, xarray, yarray);
    669 
    670     jmethodID jBuild = env->GetMethodID(jBuilderCls, "build",
    671             "()Landroid/media/VolumeShaper$Configuration;");
    672     return env->CallObjectMethod(jBuilderObj, jBuild);
    673 }
    674 
    675 jobject JAudioTrack::createVolumeShaperOperationObj(
    676         const sp<media::VolumeShaper::Operation>& operation) {
    677 
    678     JNIEnv *env = JavaVMHelper::getJNIEnv();
    679 
    680     jclass jBuilderCls = env->FindClass("android/media/VolumeShaper$Operation$Builder");
    681     jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
    682     jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor);
    683 
    684     // Set XOffset
    685     jmethodID jSetXOffset = env->GetMethodID(jBuilderCls, "setXOffset",
    686             "(F)Landroid/media/VolumeShaper$Operation$Builder;");
    687     jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetXOffset, operation->getXOffset());
    688 
    689     int32_t flags = operation->getFlags();
    690 
    691     if (operation->getReplaceId() >= 0) {
    692         jmethodID jReplace = env->GetMethodID(jBuilderCls, "replace",
    693                 "(IB)Landroid/media/VolumeShaper$Operation$Builder;");
    694         bool join = (flags | media::VolumeShaper::Operation::FLAG_JOIN) != 0;
    695         jBuilderObj = env->CallObjectMethod(jBuilderCls, jReplace, operation->getReplaceId(), join);
    696     }
    697 
    698     if (flags | media::VolumeShaper::Operation::FLAG_REVERSE) {
    699         jmethodID jReverse = env->GetMethodID(jBuilderCls, "reverse",
    700                 "()Landroid/media/VolumeShaper$Operation$Builder;");
    701         jBuilderObj = env->CallObjectMethod(jBuilderCls, jReverse);
    702     }
    703 
    704     // TODO: VolumeShaper Javadoc says "Do not call terminate() directly". Can we call this?
    705     if (flags | media::VolumeShaper::Operation::FLAG_TERMINATE) {
    706         jmethodID jTerminate = env->GetMethodID(jBuilderCls, "terminate",
    707                 "()Landroid/media/VolumeShaper$Operation$Builder;");
    708         jBuilderObj = env->CallObjectMethod(jBuilderCls, jTerminate);
    709     }
    710 
    711     if (flags | media::VolumeShaper::Operation::FLAG_DELAY) {
    712         jmethodID jDefer = env->GetMethodID(jBuilderCls, "defer",
    713                 "()Landroid/media/VolumeShaper$Operation$Builder;");
    714         jBuilderObj = env->CallObjectMethod(jBuilderCls, jDefer);
    715     }
    716 
    717     if (flags | media::VolumeShaper::Operation::FLAG_CREATE_IF_NECESSARY) {
    718         jmethodID jCreateIfNeeded = env->GetMethodID(jBuilderCls, "createIfNeeded",
    719                 "()Landroid/media/VolumeShaper$Operation$Builder;");
    720         jBuilderObj = env->CallObjectMethod(jBuilderCls, jCreateIfNeeded);
    721     }
    722 
    723     // TODO: Handle error case (can it be NULL?)
    724     jmethodID jBuild = env->GetMethodID(jBuilderCls, "build",
    725             "()Landroid/media/VolumeShaper$Operation;");
    726     return env->CallObjectMethod(jBuilderObj, jBuild);
    727 }
    728 
    729 jobject JAudioTrack::createStreamEventCallback(callback_t cbf, void* user) {
    730     JNIEnv *env = JavaVMHelper::getJNIEnv();
    731     jclass jCallbackCls = env->FindClass("android/media/MediaPlayer2$StreamEventCallback");
    732     jmethodID jCallbackCtor = env->GetMethodID(jCallbackCls, "<init>", "(JJJ)V");
    733     jobject jCallbackObj = env->NewObject(jCallbackCls, jCallbackCtor, this, cbf, user);
    734     return jCallbackObj;
    735 }
    736 
    737 jobject JAudioTrack::createCallbackExecutor() {
    738     JNIEnv *env = JavaVMHelper::getJNIEnv();
    739     jclass jExecutorsCls = env->FindClass("java/util/concurrent/Executors");
    740     jmethodID jNewSingleThreadExecutor = env->GetStaticMethodID(jExecutorsCls,
    741             "newSingleThreadExecutor", "()Ljava/util/concurrent/ExecutorService;");
    742     jobject jSingleThreadExecutorObj =
    743             env->CallStaticObjectMethod(jExecutorsCls, jNewSingleThreadExecutor);
    744     return jSingleThreadExecutorObj;
    745 }
    746 
    747 status_t JAudioTrack::javaToNativeStatus(int javaStatus) {
    748     switch (javaStatus) {
    749     case AUDIO_JAVA_SUCCESS:
    750         return NO_ERROR;
    751     case AUDIO_JAVA_BAD_VALUE:
    752         return BAD_VALUE;
    753     case AUDIO_JAVA_INVALID_OPERATION:
    754         return INVALID_OPERATION;
    755     case AUDIO_JAVA_PERMISSION_DENIED:
    756         return PERMISSION_DENIED;
    757     case AUDIO_JAVA_NO_INIT:
    758         return NO_INIT;
    759     case AUDIO_JAVA_WOULD_BLOCK:
    760         return WOULD_BLOCK;
    761     case AUDIO_JAVA_DEAD_OBJECT:
    762         return DEAD_OBJECT;
    763     default:
    764         return UNKNOWN_ERROR;
    765     }
    766 }
    767 
    768 } // namespace android
    769