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