blob: 5c0c6eab2f9eaa69ee8ed5b7d1731cdaaa3267a1 [file] [log] [blame]
Hyundo Moon660a74e2017-12-13 11:29:45 +09001/*
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"
Wei Jia51b69562018-02-05 16:17:13 -080021#include "mediaplayer2/JAudioTrack.h"
Hyundo Moon660a74e2017-12-13 11:29:45 +090022
Hyundo Moon9b26e942017-12-14 10:46:54 +090023#include <android_media_AudioErrors.h>
Dongwon Kang8144aee2018-06-28 17:40:03 -070024#include <mediaplayer2/JavaVMHelper.h>
Hyundo Moon660a74e2017-12-13 11:29:45 +090025
26namespace android {
27
Hyundo Moon9b26e942017-12-14 10:46:54 +090028// TODO: Store Java class/methodID as a member variable in the class.
Hyundo Moon660a74e2017-12-13 11:29:45 +090029// TODO: Add NULL && Exception checks after every JNI call.
30JAudioTrack::JAudioTrack( // < Usages of the arguments are below >
Hyundo Moon660a74e2017-12-13 11:29:45 +090031 uint32_t sampleRate, // AudioFormat && bufferSizeInBytes
32 audio_format_t format, // AudioFormat && bufferSizeInBytes
33 audio_channel_mask_t channelMask, // AudioFormat && bufferSizeInBytes
Hyundo Moon42a6dec2018-01-22 19:26:47 +090034 callback_t cbf, // Offload
35 void* user, // Offload
Hyundo Moon660a74e2017-12-13 11:29:45 +090036 size_t frameCount, // bufferSizeInBytes
37 audio_session_t sessionId, // AudioTrack
38 const audio_attributes_t* pAttributes, // AudioAttributes
39 float maxRequiredSpeed) { // bufferSizeInBytes
40
Dongwon Kang8144aee2018-06-28 17:40:03 -070041 JNIEnv *env = JavaVMHelper::getJNIEnv();
Dichen Zhangf8726912018-10-17 13:31:26 -070042
Hyundo Moon660a74e2017-12-13 11:29:45 +090043 jclass jAudioTrackCls = env->FindClass("android/media/AudioTrack");
Dichen Zhangf8726912018-10-17 13:31:26 -070044 mAudioTrackCls = reinterpret_cast<jclass>(env->NewGlobalRef(jAudioTrackCls));
45 env->DeleteLocalRef(jAudioTrackCls);
Hyundo Moon660a74e2017-12-13 11:29:45 +090046
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 =
Hyundo Moon9b26e942017-12-14 10:46:54 +090057 env->GetStaticMethodID(mAudioTrackCls, "getMinBufferSize", "(III)I");
58 bufferSizeInBytes = env->CallStaticIntMethod(mAudioTrackCls, jGetMinBufferSize,
Hyundo Moon660a74e2017-12-13 11:29:45 +090059 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
Dichen Zhangf8726912018-10-17 13:31:26 -070068 jobject jAudioAttributesObj = JAudioAttributes::createAudioAttributesObj(env, pAttributes);
69 mAudioAttributesObj = reinterpret_cast<jobject>(env->NewGlobalRef(jAudioAttributesObj));
70 env->DeleteLocalRef(jAudioAttributesObj);
71
Hyundo Moon660a74e2017-12-13 11:29:45 +090072 jmethodID jSetAudioAttributes = env->GetMethodID(jBuilderCls, "setAudioAttributes",
73 "(Landroid/media/AudioAttributes;)Landroid/media/AudioTrack$Builder;");
Dichen Zhangf8726912018-10-17 13:31:26 -070074 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetAudioAttributes, mAudioAttributesObj);
Hyundo Moon660a74e2017-12-13 11:29:45 +090075
76 jmethodID jSetAudioFormat = env->GetMethodID(jBuilderCls, "setAudioFormat",
77 "(Landroid/media/AudioFormat;)Landroid/media/AudioTrack$Builder;");
78 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetAudioFormat,
79 JAudioFormat::createAudioFormatObj(env, sampleRate, format, channelMask));
80
81 jmethodID jSetBufferSizeInBytes = env->GetMethodID(jBuilderCls, "setBufferSizeInBytes",
82 "(I)Landroid/media/AudioTrack$Builder;");
83 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetBufferSizeInBytes, bufferSizeInBytes);
84
85 // We only use streaming mode of Java AudioTrack.
Hyundo Moon9b26e942017-12-14 10:46:54 +090086 jfieldID jModeStream = env->GetStaticFieldID(mAudioTrackCls, "MODE_STREAM", "I");
87 jint transferMode = env->GetStaticIntField(mAudioTrackCls, jModeStream);
Hyundo Moon660a74e2017-12-13 11:29:45 +090088 jmethodID jSetTransferMode = env->GetMethodID(jBuilderCls, "setTransferMode",
89 "(I)Landroid/media/AudioTrack$Builder;");
90 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetTransferMode,
91 transferMode /* Java AudioTrack::MODE_STREAM */);
92
93 if (sessionId != 0) {
94 jmethodID jSetSessionId = env->GetMethodID(jBuilderCls, "setSessionId",
95 "(I)Landroid/media/AudioTrack$Builder;");
96 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetSessionId, sessionId);
97 }
98
Hyundo Moon42a6dec2018-01-22 19:26:47 +090099 if (cbf != NULL) {
100 jmethodID jSetOffloadedPlayback = env->GetMethodID(jBuilderCls, "setOffloadedPlayback",
101 "(Z)Landroid/media/AudioTrack$Builder;");
102 jBuilderObj = env->CallObjectMethod(jBuilderObj, jSetOffloadedPlayback, true);
103 mFlags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
104 }
105
Hyundo Moon660a74e2017-12-13 11:29:45 +0900106 jmethodID jBuild = env->GetMethodID(jBuilderCls, "build", "()Landroid/media/AudioTrack;");
Dichen Zhangf8726912018-10-17 13:31:26 -0700107 jobject jAudioTrackObj = env->CallObjectMethod(jBuilderObj, jBuild);
108 mAudioTrackObj = reinterpret_cast<jobject>(env->NewGlobalRef(jAudioTrackObj));
109 env->DeleteLocalRef(jBuilderObj);
Hyundo Moon42a6dec2018-01-22 19:26:47 +0900110
111 if (cbf != NULL) {
112 // Set offload mode callback
113 jobject jStreamEventCallbackObj = createStreamEventCallback(cbf, user);
114 jobject jExecutorObj = createCallbackExecutor();
115 jmethodID jSetStreamEventCallback = env->GetMethodID(
116 jAudioTrackCls,
117 "setStreamEventCallback",
118 "(Ljava/util/concurrent/Executor;Landroid/media/AudioTrack$StreamEventCallback;)V");
119 env->CallVoidMethod(
120 mAudioTrackObj, jSetStreamEventCallback, jExecutorObj, jStreamEventCallbackObj);
121 }
Hyundo Moon660a74e2017-12-13 11:29:45 +0900122}
123
Hyundo Moon9b26e942017-12-14 10:46:54 +0900124JAudioTrack::~JAudioTrack() {
Dongwon Kang8144aee2018-06-28 17:40:03 -0700125 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moon9b26e942017-12-14 10:46:54 +0900126 env->DeleteGlobalRef(mAudioTrackCls);
Dichen Zhangf8726912018-10-17 13:31:26 -0700127 env->DeleteGlobalRef(mAudioTrackObj);
128 env->DeleteGlobalRef(mAudioAttributesObj);
Hyundo Moon9b26e942017-12-14 10:46:54 +0900129}
130
131size_t JAudioTrack::frameCount() {
Dongwon Kang8144aee2018-06-28 17:40:03 -0700132 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moon9b26e942017-12-14 10:46:54 +0900133 jmethodID jGetBufferSizeInFrames = env->GetMethodID(
134 mAudioTrackCls, "getBufferSizeInFrames", "()I");
135 return env->CallIntMethod(mAudioTrackObj, jGetBufferSizeInFrames);
136}
137
138size_t JAudioTrack::channelCount() {
Dongwon Kang8144aee2018-06-28 17:40:03 -0700139 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moon9b26e942017-12-14 10:46:54 +0900140 jmethodID jGetChannelCount = env->GetMethodID(mAudioTrackCls, "getChannelCount", "()I");
141 return env->CallIntMethod(mAudioTrackObj, jGetChannelCount);
142}
143
Hyundo Moon904183e2018-01-21 20:43:41 +0900144uint32_t JAudioTrack::latency() {
145 // TODO: Currently hard-coded as returning zero.
146 return 0;
147}
148
Hyundo Moon9b26e942017-12-14 10:46:54 +0900149status_t JAudioTrack::getPosition(uint32_t *position) {
150 if (position == NULL) {
151 return BAD_VALUE;
152 }
153
Dongwon Kang8144aee2018-06-28 17:40:03 -0700154 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moon9b26e942017-12-14 10:46:54 +0900155 jmethodID jGetPlaybackHeadPosition = env->GetMethodID(
156 mAudioTrackCls, "getPlaybackHeadPosition", "()I");
157 *position = env->CallIntMethod(mAudioTrackObj, jGetPlaybackHeadPosition);
158
159 return NO_ERROR;
160}
161
Dichen Zhangf8726912018-10-17 13:31:26 -0700162status_t JAudioTrack::getTimestamp(AudioTimestamp& timestamp) {
Dongwon Kang8144aee2018-06-28 17:40:03 -0700163 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moonfd328172017-12-14 10:46:54 +0900164
165 jclass jAudioTimeStampCls = env->FindClass("android/media/AudioTimestamp");
166 jobject jAudioTimeStampObj = env->AllocObject(jAudioTimeStampCls);
167
Dichen Zhangf8726912018-10-17 13:31:26 -0700168 jfieldID jFramePosition = env->GetFieldID(jAudioTimeStampCls, "framePosition", "J");
169 jfieldID jNanoTime = env->GetFieldID(jAudioTimeStampCls, "nanoTime", "J");
Hyundo Moonfd328172017-12-14 10:46:54 +0900170
171 jmethodID jGetTimestamp = env->GetMethodID(mAudioTrackCls,
Dichen Zhangf8726912018-10-17 13:31:26 -0700172 "getTimestamp", "(Landroid/media/AudioTimestamp;)Z");
Hyundo Moonfd328172017-12-14 10:46:54 +0900173 bool success = env->CallBooleanMethod(mAudioTrackObj, jGetTimestamp, jAudioTimeStampObj);
174
175 if (!success) {
Dichen Zhangf8726912018-10-17 13:31:26 -0700176 return NO_INIT;
Hyundo Moonfd328172017-12-14 10:46:54 +0900177 }
178
179 long long framePosition = env->GetLongField(jAudioTimeStampObj, jFramePosition);
180 long long nanoTime = env->GetLongField(jAudioTimeStampObj, jNanoTime);
181
182 struct timespec ts;
183 const long long secondToNano = 1000000000LL; // 1E9
184 ts.tv_sec = nanoTime / secondToNano;
185 ts.tv_nsec = nanoTime % secondToNano;
186 timestamp.mTime = ts;
187 timestamp.mPosition = (uint32_t) framePosition;
188
Dichen Zhangf8726912018-10-17 13:31:26 -0700189 return NO_ERROR;
Hyundo Moonfd328172017-12-14 10:46:54 +0900190}
191
Hyundo Moon42a6dec2018-01-22 19:26:47 +0900192status_t JAudioTrack::getTimestamp(ExtendedTimestamp *timestamp __unused) {
193 // TODO: Implement this after appropriate Java AudioTrack method is available.
194 return NO_ERROR;
195}
196
Hyundo Moonfd328172017-12-14 10:46:54 +0900197status_t JAudioTrack::setPlaybackRate(const AudioPlaybackRate &playbackRate) {
198 // TODO: existing native AudioTrack returns INVALID_OPERATION on offload/direct/fast tracks.
199 // Should we do the same thing?
Dongwon Kang8144aee2018-06-28 17:40:03 -0700200 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moonfd328172017-12-14 10:46:54 +0900201
202 jclass jPlaybackParamsCls = env->FindClass("android/media/PlaybackParams");
203 jmethodID jPlaybackParamsCtor = env->GetMethodID(jPlaybackParamsCls, "<init>", "()V");
204 jobject jPlaybackParamsObj = env->NewObject(jPlaybackParamsCls, jPlaybackParamsCtor);
205
206 jmethodID jSetAudioFallbackMode = env->GetMethodID(
207 jPlaybackParamsCls, "setAudioFallbackMode", "(I)Landroid/media/PlaybackParams;");
208 jPlaybackParamsObj = env->CallObjectMethod(
209 jPlaybackParamsObj, jSetAudioFallbackMode, playbackRate.mFallbackMode);
210
211 jmethodID jSetAudioStretchMode = env->GetMethodID(
212 jPlaybackParamsCls, "setAudioStretchMode", "(I)Landroid/media/PlaybackParams;");
213 jPlaybackParamsObj = env->CallObjectMethod(
214 jPlaybackParamsObj, jSetAudioStretchMode, playbackRate.mStretchMode);
215
216 jmethodID jSetPitch = env->GetMethodID(
217 jPlaybackParamsCls, "setPitch", "(F)Landroid/media/PlaybackParams;");
218 jPlaybackParamsObj = env->CallObjectMethod(jPlaybackParamsObj, jSetPitch, playbackRate.mPitch);
219
220 jmethodID jSetSpeed = env->GetMethodID(
221 jPlaybackParamsCls, "setSpeed", "(F)Landroid/media/PlaybackParams;");
222 jPlaybackParamsObj = env->CallObjectMethod(jPlaybackParamsObj, jSetSpeed, playbackRate.mSpeed);
223
224
225 // Set this Java PlaybackParams object into Java AudioTrack.
226 jmethodID jSetPlaybackParams = env->GetMethodID(
227 mAudioTrackCls, "setPlaybackParams", "(Landroid/media/PlaybackParams;)V");
228 env->CallVoidMethod(mAudioTrackObj, jSetPlaybackParams, jPlaybackParamsObj);
229 // TODO: Should we catch the Java IllegalArgumentException?
230
231 return NO_ERROR;
232}
233
234const AudioPlaybackRate JAudioTrack::getPlaybackRate() {
Dongwon Kang8144aee2018-06-28 17:40:03 -0700235 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moonfd328172017-12-14 10:46:54 +0900236
237 jmethodID jGetPlaybackParams = env->GetMethodID(
238 mAudioTrackCls, "getPlaybackParams", "()Landroid/media/PlaybackParams;");
239 jobject jPlaybackParamsObj = env->CallObjectMethod(mAudioTrackObj, jGetPlaybackParams);
240
241 AudioPlaybackRate playbackRate;
242 jclass jPlaybackParamsCls = env->FindClass("android/media/PlaybackParams");
243
244 jmethodID jGetAudioFallbackMode = env->GetMethodID(
245 jPlaybackParamsCls, "getAudioFallbackMode", "()I");
246 // TODO: Should we enable passing AUDIO_TIMESTRETCH_FALLBACK_CUT_REPEAT?
247 // The enum is internal only, so it is not defined in PlaybackParmas.java.
248 // TODO: Is this right way to convert an int to an enum?
249 playbackRate.mFallbackMode = static_cast<AudioTimestretchFallbackMode>(
250 env->CallIntMethod(jPlaybackParamsObj, jGetAudioFallbackMode));
251
252 jmethodID jGetAudioStretchMode = env->GetMethodID(
253 jPlaybackParamsCls, "getAudioStretchMode", "()I");
254 playbackRate.mStretchMode = static_cast<AudioTimestretchStretchMode>(
255 env->CallIntMethod(jPlaybackParamsObj, jGetAudioStretchMode));
256
257 jmethodID jGetPitch = env->GetMethodID(jPlaybackParamsCls, "getPitch", "()F");
258 playbackRate.mPitch = env->CallFloatMethod(jPlaybackParamsObj, jGetPitch);
259
260 jmethodID jGetSpeed = env->GetMethodID(jPlaybackParamsCls, "getSpeed", "()F");
261 playbackRate.mSpeed = env->CallFloatMethod(jPlaybackParamsObj, jGetSpeed);
262
263 return playbackRate;
264}
265
266media::VolumeShaper::Status JAudioTrack::applyVolumeShaper(
267 const sp<media::VolumeShaper::Configuration>& configuration,
268 const sp<media::VolumeShaper::Operation>& operation) {
269
270 jobject jConfigurationObj = createVolumeShaperConfigurationObj(configuration);
271 jobject jOperationObj = createVolumeShaperOperationObj(operation);
272
273 if (jConfigurationObj == NULL || jOperationObj == NULL) {
274 return media::VolumeShaper::Status(BAD_VALUE);
275 }
276
Dongwon Kang8144aee2018-06-28 17:40:03 -0700277 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moonfd328172017-12-14 10:46:54 +0900278
279 jmethodID jCreateVolumeShaper = env->GetMethodID(mAudioTrackCls, "createVolumeShaper",
280 "(Landroid/media/VolumeShaper$Configuration;)Landroid/media/VolumeShaper;");
281 jobject jVolumeShaperObj = env->CallObjectMethod(
282 mAudioTrackObj, jCreateVolumeShaper, jConfigurationObj);
283
284 jclass jVolumeShaperCls = env->FindClass("android/media/VolumeShaper");
285 jmethodID jApply = env->GetMethodID(jVolumeShaperCls, "apply",
286 "(Landroid/media/VolumeShaper$Operation;)V");
287 env->CallVoidMethod(jVolumeShaperObj, jApply, jOperationObj);
288
289 return media::VolumeShaper::Status(NO_ERROR);
290}
291
Hyundo Moon9b26e942017-12-14 10:46:54 +0900292status_t JAudioTrack::setAuxEffectSendLevel(float level) {
Dongwon Kang8144aee2018-06-28 17:40:03 -0700293 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moon9b26e942017-12-14 10:46:54 +0900294 jmethodID jSetAuxEffectSendLevel = env->GetMethodID(
295 mAudioTrackCls, "setAuxEffectSendLevel", "(F)I");
296 int result = env->CallIntMethod(mAudioTrackObj, jSetAuxEffectSendLevel, level);
297 return javaToNativeStatus(result);
298}
299
300status_t JAudioTrack::attachAuxEffect(int effectId) {
Dongwon Kang8144aee2018-06-28 17:40:03 -0700301 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moon9b26e942017-12-14 10:46:54 +0900302 jmethodID jAttachAuxEffect = env->GetMethodID(mAudioTrackCls, "attachAuxEffect", "(I)I");
303 int result = env->CallIntMethod(mAudioTrackObj, jAttachAuxEffect, effectId);
304 return javaToNativeStatus(result);
305}
306
307status_t JAudioTrack::setVolume(float left, float right) {
Dongwon Kang8144aee2018-06-28 17:40:03 -0700308 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moon9b26e942017-12-14 10:46:54 +0900309 // TODO: Java setStereoVolume is deprecated. Do we really need this method?
310 jmethodID jSetStereoVolume = env->GetMethodID(mAudioTrackCls, "setStereoVolume", "(FF)I");
311 int result = env->CallIntMethod(mAudioTrackObj, jSetStereoVolume, left, right);
312 return javaToNativeStatus(result);
313}
314
315status_t JAudioTrack::setVolume(float volume) {
Dongwon Kang8144aee2018-06-28 17:40:03 -0700316 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moon9b26e942017-12-14 10:46:54 +0900317 jmethodID jSetVolume = env->GetMethodID(mAudioTrackCls, "setVolume", "(F)I");
318 int result = env->CallIntMethod(mAudioTrackObj, jSetVolume, volume);
319 return javaToNativeStatus(result);
320}
321
322status_t JAudioTrack::start() {
Dongwon Kang8144aee2018-06-28 17:40:03 -0700323 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moon9b26e942017-12-14 10:46:54 +0900324 jmethodID jPlay = env->GetMethodID(mAudioTrackCls, "play", "()V");
325 // TODO: Should we catch the Java IllegalStateException from play()?
326 env->CallVoidMethod(mAudioTrackObj, jPlay);
327 return NO_ERROR;
328}
329
Hyundo Moonfd328172017-12-14 10:46:54 +0900330ssize_t JAudioTrack::write(const void* buffer, size_t size, bool blocking) {
331 if (buffer == NULL) {
332 return BAD_VALUE;
333 }
334
Dongwon Kang8144aee2018-06-28 17:40:03 -0700335 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moonfd328172017-12-14 10:46:54 +0900336 jbyteArray jAudioData = env->NewByteArray(size);
337 env->SetByteArrayRegion(jAudioData, 0, size, (jbyte *) buffer);
338
339 jclass jByteBufferCls = env->FindClass("java/nio/ByteBuffer");
340 jmethodID jWrap = env->GetStaticMethodID(jByteBufferCls, "wrap", "([B)Ljava/nio/ByteBuffer;");
341 jobject jByteBufferObj = env->CallStaticObjectMethod(jByteBufferCls, jWrap, jAudioData);
342
343 int writeMode = 0;
344 if (blocking) {
345 jfieldID jWriteBlocking = env->GetStaticFieldID(mAudioTrackCls, "WRITE_BLOCKING", "I");
346 writeMode = env->GetStaticIntField(mAudioTrackCls, jWriteBlocking);
347 } else {
348 jfieldID jWriteNonBlocking = env->GetStaticFieldID(
349 mAudioTrackCls, "WRITE_NON_BLOCKING", "I");
350 writeMode = env->GetStaticIntField(mAudioTrackCls, jWriteNonBlocking);
351 }
352
353 jmethodID jWrite = env->GetMethodID(mAudioTrackCls, "write", "(Ljava/nio/ByteBuffer;II)I");
354 int result = env->CallIntMethod(mAudioTrackObj, jWrite, jByteBufferObj, size, writeMode);
355
356 if (result >= 0) {
357 return result;
358 } else {
359 return javaToNativeStatus(result);
360 }
361}
362
Hyundo Moon660a74e2017-12-13 11:29:45 +0900363void JAudioTrack::stop() {
Dongwon Kang8144aee2018-06-28 17:40:03 -0700364 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moon9b26e942017-12-14 10:46:54 +0900365 jmethodID jStop = env->GetMethodID(mAudioTrackCls, "stop", "()V");
Hyundo Moon660a74e2017-12-13 11:29:45 +0900366 env->CallVoidMethod(mAudioTrackObj, jStop);
Hyundo Moon9b26e942017-12-14 10:46:54 +0900367 // TODO: Should we catch IllegalStateException?
368}
369
370// TODO: Is the right implementation?
371bool JAudioTrack::stopped() const {
372 return !isPlaying();
373}
374
375void JAudioTrack::flush() {
Dongwon Kang8144aee2018-06-28 17:40:03 -0700376 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moon9b26e942017-12-14 10:46:54 +0900377 jmethodID jFlush = env->GetMethodID(mAudioTrackCls, "flush", "()V");
378 env->CallVoidMethod(mAudioTrackObj, jFlush);
379}
380
381void JAudioTrack::pause() {
Dongwon Kang8144aee2018-06-28 17:40:03 -0700382 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moon9b26e942017-12-14 10:46:54 +0900383 jmethodID jPause = env->GetMethodID(mAudioTrackCls, "pause", "()V");
384 env->CallVoidMethod(mAudioTrackObj, jPause);
385 // TODO: Should we catch IllegalStateException?
386}
387
388bool JAudioTrack::isPlaying() const {
Dongwon Kang8144aee2018-06-28 17:40:03 -0700389 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moon9b26e942017-12-14 10:46:54 +0900390 jmethodID jGetPlayState = env->GetMethodID(mAudioTrackCls, "getPlayState", "()I");
391 int currentPlayState = env->CallIntMethod(mAudioTrackObj, jGetPlayState);
392
393 // TODO: In Java AudioTrack, there is no STOPPING state.
394 // This means while stopping, isPlaying() will return different value in two class.
395 // - in existing native AudioTrack: true
396 // - in JAudioTrack: false
397 // If not okay, also modify the implementation of stopped().
398 jfieldID jPlayStatePlaying = env->GetStaticFieldID(mAudioTrackCls, "PLAYSTATE_PLAYING", "I");
399 int statePlaying = env->GetStaticIntField(mAudioTrackCls, jPlayStatePlaying);
400 return currentPlayState == statePlaying;
401}
402
403uint32_t JAudioTrack::getSampleRate() {
Dongwon Kang8144aee2018-06-28 17:40:03 -0700404 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moon9b26e942017-12-14 10:46:54 +0900405 jmethodID jGetSampleRate = env->GetMethodID(mAudioTrackCls, "getSampleRate", "()I");
406 return env->CallIntMethod(mAudioTrackObj, jGetSampleRate);
407}
408
Hyundo Moonfd328172017-12-14 10:46:54 +0900409status_t JAudioTrack::getBufferDurationInUs(int64_t *duration) {
410 if (duration == nullptr) {
411 return BAD_VALUE;
412 }
413
Dongwon Kang8144aee2018-06-28 17:40:03 -0700414 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moonfd328172017-12-14 10:46:54 +0900415 jmethodID jGetBufferSizeInFrames = env->GetMethodID(
416 mAudioTrackCls, "getBufferSizeInFrames", "()I");
417 int bufferSizeInFrames = env->CallIntMethod(mAudioTrackObj, jGetBufferSizeInFrames);
418
419 const double secondToMicro = 1000000LL; // 1E6
420 int sampleRate = JAudioTrack::getSampleRate();
421 float speed = JAudioTrack::getPlaybackRate().mSpeed;
422
423 *duration = (int64_t) (bufferSizeInFrames * secondToMicro / (sampleRate * speed));
424 return NO_ERROR;
425}
426
Hyundo Moon9b26e942017-12-14 10:46:54 +0900427audio_format_t JAudioTrack::format() {
Dongwon Kang8144aee2018-06-28 17:40:03 -0700428 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moon9b26e942017-12-14 10:46:54 +0900429 jmethodID jGetAudioFormat = env->GetMethodID(mAudioTrackCls, "getAudioFormat", "()I");
430 int javaFormat = env->CallIntMethod(mAudioTrackObj, jGetAudioFormat);
431 return audioFormatToNative(javaFormat);
432}
433
Dichen Zhangf8726912018-10-17 13:31:26 -0700434size_t JAudioTrack::frameSize() {
435 JNIEnv *env = JavaVMHelper::getJNIEnv();
436
437 // TODO: Calculated here implementing the logic in AudioTrack.java
438 // wait for AudioTrack.java exposing this parameter (i.e. getFrameSizeInBtytes())
439 jmethodID jGetAudioFormat = env->GetMethodID(mAudioTrackCls, "getAudioFormat", "()I");
440 int javaFormat = env->CallIntMethod(mAudioTrackObj, jGetAudioFormat);
441
442 jclass jAudioFormatCls = env->FindClass("android/media/AudioFormat");
443 jmethodID jIsEncodingLinearFrames = env->GetStaticMethodID(
444 jAudioFormatCls, "isEncodingLinearFrames", "(I)Z");
445 jboolean javaIsEncodingLinearFrames = env->CallStaticBooleanMethod(
446 jAudioFormatCls, jIsEncodingLinearFrames, javaFormat);
447
448 if (javaIsEncodingLinearFrames == false) {
449 return 1;
450 }
451
452 jmethodID jGetBytesPerSample = env->GetStaticMethodID(jAudioFormatCls,
453 "getBytesPerSample", "(I)I");
454 int javaBytesPerSample = env->CallStaticIntMethod(jAudioFormatCls,
455 jGetBytesPerSample, javaFormat);
456
457 jmethodID jGetChannelCount = env->GetMethodID(mAudioTrackCls, "getChannelCount", "()I");
458 int javaChannelCount = env->CallIntMethod(mAudioTrackObj, jGetChannelCount);
459
460 return javaChannelCount * javaBytesPerSample;
461}
462
Hyundo Moon904183e2018-01-21 20:43:41 +0900463status_t JAudioTrack::dump(int fd, const Vector<String16>& args __unused) const
464{
465 String8 result;
466
467 result.append(" JAudioTrack::dump\n");
468
469 // TODO: Remove logs that includes unavailable information from below.
470// result.appendFormat(" status(%d), state(%d), session Id(%d), flags(%#x)\n",
471// mStatus, mState, mSessionId, mFlags);
Hyundo Moon904183e2018-01-21 20:43:41 +0900472// result.appendFormat(" format(%#x), channel mask(%#x), channel count(%u)\n",
473// format(), mChannelMask, channelCount());
474// result.appendFormat(" sample rate(%u), original sample rate(%u), speed(%f)\n",
475// getSampleRate(), mOriginalSampleRate, mPlaybackRate.mSpeed);
476// result.appendFormat(" frame count(%zu), req. frame count(%zu)\n",
477// frameCount(), mReqFrameCount);
478// result.appendFormat(" notif. frame count(%u), req. notif. frame count(%u),"
479// " req. notif. per buff(%u)\n",
480// mNotificationFramesAct, mNotificationFramesReq, mNotificationsPerBufferReq);
481// result.appendFormat(" latency (%d), selected device Id(%d), routed device Id(%d)\n",
482// latency(), mSelectedDeviceId, getRoutedDeviceId());
483// result.appendFormat(" output(%d) AF latency (%u) AF frame count(%zu) AF SampleRate(%u)\n",
484// mOutput, mAfLatency, mAfFrameCount, mAfSampleRate);
485 ::write(fd, result.string(), result.size());
486 return NO_ERROR;
487}
488
489audio_port_handle_t JAudioTrack::getRoutedDeviceId() {
Dongwon Kang8144aee2018-06-28 17:40:03 -0700490 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moon904183e2018-01-21 20:43:41 +0900491 jmethodID jGetRoutedDevice = env->GetMethodID(mAudioTrackCls, "getRoutedDevice",
492 "()Landroid/media/AudioDeviceInfo;");
493 jobject jAudioDeviceInfoObj = env->CallObjectMethod(mAudioTrackObj, jGetRoutedDevice);
494 if (env->IsSameObject(jAudioDeviceInfoObj, NULL)) {
495 return AUDIO_PORT_HANDLE_NONE;
496 }
497
Dichen Zhangf8726912018-10-17 13:31:26 -0700498 jclass jAudioDeviceInfoCls = env->FindClass("android/media/AudioDeviceInfo");
Hyundo Moon904183e2018-01-21 20:43:41 +0900499 jmethodID jGetId = env->GetMethodID(jAudioDeviceInfoCls, "getId", "()I");
500 jint routedDeviceId = env->CallIntMethod(jAudioDeviceInfoObj, jGetId);
501 return routedDeviceId;
502}
503
Hyundo Moon42a6dec2018-01-22 19:26:47 +0900504audio_session_t JAudioTrack::getAudioSessionId() {
Dongwon Kang8144aee2018-06-28 17:40:03 -0700505 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moon42a6dec2018-01-22 19:26:47 +0900506 jmethodID jGetAudioSessionId = env->GetMethodID(mAudioTrackCls, "getAudioSessionId", "()I");
507 jint sessionId = env->CallIntMethod(mAudioTrackObj, jGetAudioSessionId);
508 return (audio_session_t) sessionId;
509}
510
511status_t JAudioTrack::setOutputDevice(audio_port_handle_t deviceId) {
Dongwon Kang8144aee2018-06-28 17:40:03 -0700512 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moon42a6dec2018-01-22 19:26:47 +0900513 jclass jMP2ImplCls = env->FindClass("android/media/MediaPlayer2Impl");
Dichen Zhangf8726912018-10-17 13:31:26 -0700514 jmethodID jSetAudioOutputDeviceById = env->GetStaticMethodID(
Hyundo Moon42a6dec2018-01-22 19:26:47 +0900515 jMP2ImplCls, "setAudioOutputDeviceById", "(Landroid/media/AudioTrack;I)Z");
516 jboolean result = env->CallStaticBooleanMethod(
517 jMP2ImplCls, jSetAudioOutputDeviceById, mAudioTrackObj, deviceId);
518 return result == true ? NO_ERROR : BAD_VALUE;
519}
520
Dichen Zhangf8726912018-10-17 13:31:26 -0700521audio_stream_type_t JAudioTrack::getAudioStreamType() {
522 JNIEnv *env = JavaVMHelper::getJNIEnv();
523 jclass jAudioAttributesCls = env->FindClass("android/media/AudioAttributes");
524 jmethodID jGetVolumeControlStream = env->GetMethodID(jAudioAttributesCls,
525 "getVolumeControlStream", "()I");
526 int javaAudioStreamType = env->CallIntMethod(mAudioAttributesObj, jGetVolumeControlStream);
527 return (audio_stream_type_t)javaAudioStreamType;
528}
529
Hyundo Moon42a6dec2018-01-22 19:26:47 +0900530status_t JAudioTrack::pendingDuration(int32_t *msec) {
531 if (msec == nullptr) {
532 return BAD_VALUE;
533 }
534
535 bool isPurePcmData = audio_is_linear_pcm(format()) && (getFlags() & AUDIO_FLAG_HW_AV_SYNC) == 0;
536 if (!isPurePcmData) {
537 return INVALID_OPERATION;
538 }
539
540 // TODO: Need to know the difference btw. client and server time.
541 // If getTimestamp(ExtendedTimestamp) is ready, and un-comment below and modify appropriately.
542 // (copied from AudioTrack.cpp)
543
544// ExtendedTimestamp ets;
545// ExtendedTimestamp::LOCATION location = ExtendedTimestamp::LOCATION_SERVER;
546// if (getTimestamp_l(&ets) == OK && ets.mTimeNs[location] > 0) {
547// int64_t diff = ets.mPosition[ExtendedTimestamp::LOCATION_CLIENT]
548// - ets.mPosition[location];
549// if (diff < 0) {
550// *msec = 0;
551// } else {
552// // ms is the playback time by frames
553// int64_t ms = (int64_t)((double)diff * 1000 /
554// ((double)mSampleRate * mPlaybackRate.mSpeed));
555// // clockdiff is the timestamp age (negative)
556// int64_t clockdiff = (mState != STATE_ACTIVE) ? 0 :
557// ets.mTimeNs[location]
558// + ets.mTimebaseOffset[ExtendedTimestamp::TIMEBASE_MONOTONIC]
559// - systemTime(SYSTEM_TIME_MONOTONIC);
560//
561// //ALOGV("ms: %lld clockdiff: %lld", (long long)ms, (long long)clockdiff);
562// static const int NANOS_PER_MILLIS = 1000000;
563// *msec = (int32_t)(ms + clockdiff / NANOS_PER_MILLIS);
564// }
565// return NO_ERROR;
566// }
567
568 return NO_ERROR;
569}
570
Dichen Zhangf8726912018-10-17 13:31:26 -0700571status_t JAudioTrack::addAudioDeviceCallback(jobject listener, jobject handler) {
572 JNIEnv *env = JavaVMHelper::getJNIEnv();
573 jmethodID jAddOnRoutingChangedListener = env->GetMethodID(mAudioTrackCls,
574 "addOnRoutingChangedListener",
575 "(Landroid/media/AudioRouting$OnRoutingChangedListener;Landroid/os/Handler;)V");
576 env->CallVoidMethod(mAudioTrackObj, jAddOnRoutingChangedListener, listener, handler);
Hyundo Moon42a6dec2018-01-22 19:26:47 +0900577 return NO_ERROR;
578}
579
Dichen Zhangf8726912018-10-17 13:31:26 -0700580status_t JAudioTrack::removeAudioDeviceCallback(jobject listener) {
581 JNIEnv *env = JavaVMHelper::getJNIEnv();
582 jmethodID jRemoveOnRoutingChangedListener = env->GetMethodID(mAudioTrackCls,
583 "removeOnRoutingChangedListener",
584 "(Landroid/media/AudioRouting$OnRoutingChangedListener;)V");
585 env->CallVoidMethod(mAudioTrackObj, jRemoveOnRoutingChangedListener, listener);
Hyundo Moon42a6dec2018-01-22 19:26:47 +0900586 return NO_ERROR;
587}
588
Dichen Zhangf8726912018-10-17 13:31:26 -0700589void JAudioTrack::registerRoutingDelegates(
590 std::vector<std::pair<jobject, jobject>>& routingDelegates) {
591 for (std::vector<std::pair<jobject, jobject>>::iterator it = routingDelegates.begin();
592 it != routingDelegates.end(); it++) {
593 addAudioDeviceCallback(it->second, getHandler(it->second));
594 }
595}
596
597/////////////////////////////////////////////////////////////
598/// Static methods begin ///
599/////////////////////////////////////////////////////////////
600jobject JAudioTrack::getListener(const jobject routingDelegateObj) {
601 JNIEnv *env = JavaVMHelper::getJNIEnv();
602 jclass jRoutingDelegateCls = env->FindClass("android/media/RoutingDelegate");
603 jmethodID jGetListener = env->GetMethodID(jRoutingDelegateCls,
604 "getListener", "()Landroid/media/AudioRouting$OnRoutingChangedListener;");
605 return env->CallObjectMethod(routingDelegateObj, jGetListener);
606}
607
608jobject JAudioTrack::getHandler(const jobject routingDelegateObj) {
609 JNIEnv *env = JavaVMHelper::getJNIEnv();
610 jclass jRoutingDelegateCls = env->FindClass("android/media/RoutingDelegate");
611 jmethodID jGetHandler = env->GetMethodID(jRoutingDelegateCls,
612 "getHandler", "()Landroid/os/Handler;");
613 return env->CallObjectMethod(routingDelegateObj, jGetHandler);
614}
615
616jobject JAudioTrack::addGlobalRef(const jobject obj) {
617 JNIEnv *env = JavaVMHelper::getJNIEnv();
618 return reinterpret_cast<jobject>(env->NewGlobalRef(obj));
619}
620
621status_t JAudioTrack::removeGlobalRef(const jobject obj) {
622 if (obj == NULL) {
623 return BAD_VALUE;
624 }
625 JNIEnv *env = JavaVMHelper::getJNIEnv();
626 env->DeleteGlobalRef(obj);
627 return NO_ERROR;
628}
629
630jobject JAudioTrack::findByKey(std::vector<std::pair<jobject, jobject>>& mp, const jobject key) {
631 JNIEnv *env = JavaVMHelper::getJNIEnv();
632 for (std::vector<std::pair<jobject, jobject>>::iterator it = mp.begin(); it != mp.end(); it++) {
633 if (env->IsSameObject(it->first, key)) {
634 return it->second;
635 }
636 }
637 return nullptr;
638}
639
640void JAudioTrack::eraseByKey(std::vector<std::pair<jobject, jobject>>& mp, const jobject key) {
641 JNIEnv *env = JavaVMHelper::getJNIEnv();
642 for (std::vector<std::pair<jobject, jobject>>::iterator it = mp.begin(); it != mp.end(); it++) {
643 if (env->IsSameObject(it->first, key)) {
644 mp.erase(it);
645 return;
646 }
647 }
648}
649
Hyundo Moon42a6dec2018-01-22 19:26:47 +0900650/////////////////////////////////////////////////////////////
651/// Private method begins ///
652/////////////////////////////////////////////////////////////
653
Hyundo Moonfd328172017-12-14 10:46:54 +0900654jobject JAudioTrack::createVolumeShaperConfigurationObj(
655 const sp<media::VolumeShaper::Configuration>& config) {
656
657 // TODO: Java VolumeShaper's setId() / setOptionFlags() are hidden.
658 if (config == NULL || config->getType() == media::VolumeShaper::Configuration::TYPE_ID) {
659 return NULL;
660 }
661
Dongwon Kang8144aee2018-06-28 17:40:03 -0700662 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moonfd328172017-12-14 10:46:54 +0900663
664 // Referenced "android_media_VolumeShaper.h".
665 jfloatArray xarray = nullptr;
666 jfloatArray yarray = nullptr;
667 if (config->getType() == media::VolumeShaper::Configuration::TYPE_SCALE) {
668 // convert curve arrays
669 xarray = env->NewFloatArray(config->size());
670 yarray = env->NewFloatArray(config->size());
671 float * const x = env->GetFloatArrayElements(xarray, nullptr /* isCopy */);
672 float * const y = env->GetFloatArrayElements(yarray, nullptr /* isCopy */);
673 float *xptr = x, *yptr = y;
674 for (const auto &pt : *config.get()) {
675 *xptr++ = pt.first;
676 *yptr++ = pt.second;
677 }
678 env->ReleaseFloatArrayElements(xarray, x, 0 /* mode */);
679 env->ReleaseFloatArrayElements(yarray, y, 0 /* mode */);
680 }
681
682 jclass jBuilderCls = env->FindClass("android/media/VolumeShaper$Configuration$Builder");
683 jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
684 jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor);
685
686 jmethodID jSetDuration = env->GetMethodID(jBuilderCls, "setDuration",
687 "(L)Landroid/media/VolumeShaper$Configuration$Builder;");
688 jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetDuration, (jlong) config->getDurationMs());
689
690 jmethodID jSetInterpolatorType = env->GetMethodID(jBuilderCls, "setInterpolatorType",
691 "(I)Landroid/media/VolumeShaper$Configuration$Builder;");
692 jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetInterpolatorType,
693 config->getInterpolatorType());
694
695 jmethodID jSetCurve = env->GetMethodID(jBuilderCls, "setCurve",
696 "([F[F)Landroid/media/VolumeShaper$Configuration$Builder;");
697 jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetCurve, xarray, yarray);
698
699 jmethodID jBuild = env->GetMethodID(jBuilderCls, "build",
700 "()Landroid/media/VolumeShaper$Configuration;");
701 return env->CallObjectMethod(jBuilderObj, jBuild);
702}
703
704jobject JAudioTrack::createVolumeShaperOperationObj(
705 const sp<media::VolumeShaper::Operation>& operation) {
706
Dongwon Kang8144aee2018-06-28 17:40:03 -0700707 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moonfd328172017-12-14 10:46:54 +0900708
709 jclass jBuilderCls = env->FindClass("android/media/VolumeShaper$Operation$Builder");
710 jmethodID jBuilderCtor = env->GetMethodID(jBuilderCls, "<init>", "()V");
711 jobject jBuilderObj = env->NewObject(jBuilderCls, jBuilderCtor);
712
713 // Set XOffset
714 jmethodID jSetXOffset = env->GetMethodID(jBuilderCls, "setXOffset",
715 "(F)Landroid/media/VolumeShaper$Operation$Builder;");
716 jBuilderObj = env->CallObjectMethod(jBuilderCls, jSetXOffset, operation->getXOffset());
717
718 int32_t flags = operation->getFlags();
719
720 if (operation->getReplaceId() >= 0) {
721 jmethodID jReplace = env->GetMethodID(jBuilderCls, "replace",
722 "(IB)Landroid/media/VolumeShaper$Operation$Builder;");
723 bool join = (flags | media::VolumeShaper::Operation::FLAG_JOIN) != 0;
724 jBuilderObj = env->CallObjectMethod(jBuilderCls, jReplace, operation->getReplaceId(), join);
725 }
726
727 if (flags | media::VolumeShaper::Operation::FLAG_REVERSE) {
728 jmethodID jReverse = env->GetMethodID(jBuilderCls, "reverse",
729 "()Landroid/media/VolumeShaper$Operation$Builder;");
730 jBuilderObj = env->CallObjectMethod(jBuilderCls, jReverse);
731 }
732
733 // TODO: VolumeShaper Javadoc says "Do not call terminate() directly". Can we call this?
734 if (flags | media::VolumeShaper::Operation::FLAG_TERMINATE) {
735 jmethodID jTerminate = env->GetMethodID(jBuilderCls, "terminate",
736 "()Landroid/media/VolumeShaper$Operation$Builder;");
737 jBuilderObj = env->CallObjectMethod(jBuilderCls, jTerminate);
738 }
739
740 if (flags | media::VolumeShaper::Operation::FLAG_DELAY) {
741 jmethodID jDefer = env->GetMethodID(jBuilderCls, "defer",
742 "()Landroid/media/VolumeShaper$Operation$Builder;");
743 jBuilderObj = env->CallObjectMethod(jBuilderCls, jDefer);
744 }
745
746 if (flags | media::VolumeShaper::Operation::FLAG_CREATE_IF_NECESSARY) {
747 jmethodID jCreateIfNeeded = env->GetMethodID(jBuilderCls, "createIfNeeded",
748 "()Landroid/media/VolumeShaper$Operation$Builder;");
749 jBuilderObj = env->CallObjectMethod(jBuilderCls, jCreateIfNeeded);
750 }
751
752 // TODO: Handle error case (can it be NULL?)
753 jmethodID jBuild = env->GetMethodID(jBuilderCls, "build",
754 "()Landroid/media/VolumeShaper$Operation;");
755 return env->CallObjectMethod(jBuilderObj, jBuild);
756}
757
Hyundo Moon42a6dec2018-01-22 19:26:47 +0900758jobject JAudioTrack::createStreamEventCallback(callback_t cbf, void* user) {
Dongwon Kang8144aee2018-06-28 17:40:03 -0700759 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moon42a6dec2018-01-22 19:26:47 +0900760 jclass jCallbackCls = env->FindClass("android/media/MediaPlayer2Impl$StreamEventCallback");
761 jmethodID jCallbackCtor = env->GetMethodID(jCallbackCls, "<init>", "(JJJ)V");
762 jobject jCallbackObj = env->NewObject(jCallbackCls, jCallbackCtor, this, cbf, user);
763 return jCallbackObj;
764}
765
766jobject JAudioTrack::createCallbackExecutor() {
Dongwon Kang8144aee2018-06-28 17:40:03 -0700767 JNIEnv *env = JavaVMHelper::getJNIEnv();
Hyundo Moon42a6dec2018-01-22 19:26:47 +0900768 jclass jExecutorsCls = env->FindClass("java/util/concurrent/Executors");
769 jmethodID jNewSingleThreadExecutor = env->GetStaticMethodID(jExecutorsCls,
770 "newSingleThreadExecutor", "()Ljava/util/concurrent/ExecutorService;");
771 jobject jSingleThreadExecutorObj =
772 env->CallStaticObjectMethod(jExecutorsCls, jNewSingleThreadExecutor);
773 return jSingleThreadExecutorObj;
774}
775
Hyundo Moon9b26e942017-12-14 10:46:54 +0900776status_t JAudioTrack::javaToNativeStatus(int javaStatus) {
777 switch (javaStatus) {
778 case AUDIO_JAVA_SUCCESS:
779 return NO_ERROR;
780 case AUDIO_JAVA_BAD_VALUE:
781 return BAD_VALUE;
782 case AUDIO_JAVA_INVALID_OPERATION:
783 return INVALID_OPERATION;
784 case AUDIO_JAVA_PERMISSION_DENIED:
785 return PERMISSION_DENIED;
786 case AUDIO_JAVA_NO_INIT:
787 return NO_INIT;
788 case AUDIO_JAVA_WOULD_BLOCK:
789 return WOULD_BLOCK;
790 case AUDIO_JAVA_DEAD_OBJECT:
791 return DEAD_OBJECT;
792 default:
793 return UNKNOWN_ERROR;
794 }
Hyundo Moon660a74e2017-12-13 11:29:45 +0900795}
796
797} // namespace android