c++
struct audio_track_fields_t {
jmethodID postNativeEventInJava; // Java 的 postEventFromNative 方法 ------ native→Java 回调入口
jfieldID nativeTrackInJavaObj; // Java 的 mNativeTrackInJavaObj(long)------ 存 native AudioTrack 指针的"句柄"
jfieldID jniData; // Java 的 mJniData(long)------ 存 AudioTrackJniStorage 指针(额外 native 资源)
jfieldID fieldStreamType; // Java 的 mStreamType(int)------ 缓存流类型字段
};
static audio_track_fields_t javaAudioTrackFields; // ← 全局实例
|JNI字段|类型|Java侧对应|用途| |nativeTrackInJavaObj|jfieldID|AudioTrack.mNativeTrackInJavaObj(long)|long 句柄:C++ 指针 ↔ Java 对象 |jniData|jfieldID|AudioTrack.mJniData(long)|存 AudioTrackJniStorage(弱引用、回调存储等) |fieldStreamType|jfieldID|AudioTrack.mStreamType(int)|缓存 int 字段,随时读 |postNativeEventInJava|jmethodID|AudioTrack.postEventFromNative(...)|native→Java 回调:native 线程里调这个方法
c++
// 先来总览把
// 日志 + 校验 jSession
// 取 session 值 (GetPrimitiveArrayCritical)
// 拿 Java 类对象 clazz
// 分支:nativeAudioTrack == 0 ?
// ├─ 是(新建):校验参数 → 转声道/格式 → 计算 frameCount
// │ → new AudioTrack() → 解析 AudioAttributes
// │ → 建 AudioTrackJniStorage → 构造 offloadInfo
// │ → lpTrack->set()(STREAM / STATIC 两分支)
// └─ 否(包装):直接用传入的 native 指针
// 共同段:设置回调 → 回写 session / sampleRate
// → 注册 cookie → SetLongField×2 → SetIntField streamType
// 成功返回 AUDIO_JAVA_SUCCESS
// 失败:goto native_init_failure 统一清理
// ----------------------------------------------------------------------------
static jint android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this,
jobject jaa, jintArray jSampleRate,
jint channelPositionMask, jint channelIndexMask,
jint audioFormat, jint buffSizeInBytes, jint memoryMode,
jintArray jSession, jlong nativeAudioTrack,
jboolean offload, jint encapsulationMode,
jobject tunerConfiguration, jstring opPackageName) {
ALOGV("sampleRates=%p, channel mask=%x, index mask=%x, audioFormat(Java)=%d, buffSize=%d,"
" nativeAudioTrack=0x%" PRIX64 ", offload=%d encapsulationMode=%d tuner=%p",
jSampleRate, channelPositionMask, channelIndexMask, audioFormat, buffSizeInBytes,
nativeAudioTrack, offload, encapsulationMode, tunerConfiguration);
// 任何 JNI 函数第一步都是检查关键参数,非法直接返回错误码(不抛异常)
// TunerConfigurationHelper:把 Java 的 tunerConfiguration 对象包装成 C++ helper(Android 12 新增的调谐器配置,offload 音视频场景用),之后 tunerHelper.getContentId() 取内容 ID
if (jSession == NULL) {
ALOGE("Error creating AudioTrack: invalid session ID pointer");
return (jint) AUDIO_JAVA_ERROR;
}
const TunerConfigurationHelper tunerHelper(env, tunerConfiguration);
// 第一次取 session ------ GetPrimitiveArrayCritical
// 这是比GetIntArrayElements 更极端的性能手段:GetPrimitiveArrayCritical可能直接返回数组在Java堆里的原始指针 零拷贝
// 代价是临界区内禁止调用任何其他JNI函数 否则可能会触发GC导致指针失效 所以你会看到它取出来立刻用、立刻释放 ------ 临界区极短
// 这里 只读 sessionId 可能是 0 标识让native分配
// Release 第三个参数 0 标识写回。注意它是 JNI_ABORT的反义词:0 = 把修改复制回 Java, JNI_ABORT = 丢弃修改
jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
if (nSession == NULL) {
ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
return (jint) AUDIO_JAVA_ERROR;
}
audio_session_t sessionId = (audio_session_t) nSession[0];
env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
nSession = NULL;
AudioTrackJniStorage* lpJniStorage = NULL;
// 拿Java类对象
// 拿到AudioTrack的Java类,后面存全局引用、设置回调时要用
jclass clazz = env->GetObjectClass(thiz);
if (clazz == NULL) {
ALOGE("Can't find %s when setting up callback.", kClassPathName);
return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
}
// if we pass in an existing *Native* AudioTrack, we don't need to create/initialize one.
sp<AudioTrack> lpTrack;
// 新建分支的参数校验与转换
if (nativeAudioTrack == 0) { // // 0 = 要在 native 新建(就是你 Java 传的 0)
if (jaa == 0) { // // AudioAttributes 必填
ALOGE("Error creating AudioTrack: invalid audio attributes");
return (jint) AUDIO_JAVA_ERROR;
}
if (jSampleRate == 0) {
ALOGE("Error creating AudioTrack: invalid sample rates");
return (jint) AUDIO_JAVA_ERROR;
}
//读 sampleRate 用 GetIntArrayElements + JNI_ABORT(只读丢弃写回),读 session 用 GetPrimitiveArrayCritical + 0(要写回)
int* sampleRates = env->GetIntArrayElements(jSampleRate, NULL);
int sampleRateInHertz = sampleRates[0];
env->ReleaseIntArrayElements(jSampleRate, sampleRates, JNI_ABORT);
// ↑ JNI_ABORT:只读,不把修改写回 Java(优化)
// Invalid channel representations are caught by !audio_is_output_channel() below.
// Java 常量 → native 常量
// Java的CHANNEL_OUT_*、ENCODING_*常量和C++的audio_channel_mask_t、audio_format_t是两套完全不同
audio_channel_mask_t nativeChannelMask = nativeChannelMaskFromJavaChannelMasks(
channelPositionMask, channelIndexMask);
if (!audio_is_output_channel(nativeChannelMask)) {
ALOGE("Error creating AudioTrack: invalid native channel mask %#x.", nativeChannelMask);
return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDCHANNELMASK;
} // 非法声道
uint32_t channelCount = audio_channel_count_from_out_mask(nativeChannelMask);
// check the format.
// This function was called from Java, so we compare the format against the Java constants
audio_format_t format = audioFormatToNative(audioFormat); // Java encoding → audio_format_t
if (format == AUDIO_FORMAT_INVALID) {
ALOGE("Error creating AudioTrack: unsupported audio format %d.", audioFormat);
return (jint) AUDIOTRACK_ERROR_SETUP_INVALIDFORMAT;
}
// compute the frame count
// frameCount帧数计算
// native AudioTrack 内部按帧 一个采样周期的所有声道样本 管理缓冲,所以要把Java给的字节数换算成帧数
// 压缩流 AAC 帧大小不定 就退化为字节数
size_t frameCount;
if (audio_has_proportional_frames(format)) {
const size_t bytesPerSample = audio_bytes_per_sample(format);
frameCount = buffSizeInBytes / (channelCount * bytesPerSample);
} else {
frameCount = buffSizeInBytes;
}
// create the native AudioTrack object
// 创建 native 对象 + 解析 AudioAttributes
// ScopedUtfChars RAII封装 GetStringUTFChars/ReleaseStringUTFChars,出作用域自动释放
// AttributionSourceState Android 12 起引入的"调用来源"结构(包名 + token),用于权限/归因审计。sp<BBinder>::make() 造一个 token
ScopedUtfChars opPackageNameStr(env, opPackageName);
// TODO b/182469354: make consistent with AudioRecord
AttributionSourceState attributionSource;
attributionSource.packageName = std::string(opPackageNameStr.c_str());
attributionSource.token = sp<BBinder>::make();
lpTrack = new AudioTrack(attributionSource); // ← libaudioclient 的对象!注意这里只传了 attributionSource
// read the AudioAttributes values
auto paa = JNIAudioAttributeHelper::makeUnique();
jint jStatus = JNIAudioAttributeHelper::nativeFromJava(env, jaa, paa.get()); // AudioAttributes → audio_attributes_t
if (jStatus != (jint)AUDIO_JAVA_SUCCESS) {
return jStatus;
}
ALOGV("AudioTrack_setup for usage=%d content=%d flags=0x%#x tags=%s",
paa->usage, paa->content_type, paa->flags, paa->tags);
// initialize the callback information:
// this data will be passed with every AudioTrack callback
// AudioTrackJniStorage ------ native 侧的"上下文包"
// NewGlobalRef(weak_this) 是对 Java 的 WeakReference 对象做的强全局引用------不是对 AudioTrack 本体!所以 AudioTrack 对象本身依然可以被 GC 回收(注释也写着 "we use a weak reference so the AudioTrack object can be garbage collected")。Java 侧的 WeakReference + JNI 侧的全局引用 = 组合拳:native 持有的是"通往弱引用的强引用",既不泄漏 AudioTrack,又能随时通过它找到对象。
// busy 标志:回调重入保护
lpJniStorage = new AudioTrackJniStorage();
lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
// we use a weak reference so the AudioTrack object can be garbage collected.
lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
lpJniStorage->mCallbackData.isOffload = offload;
lpJniStorage->mCallbackData.busy = false;
// 构造 offloadInfo(offload / 封装两种场景)
audio_offload_info_t offloadInfo;
if (offload == JNI_TRUE) {
offloadInfo = AUDIO_INFO_INITIALIZER;
offloadInfo.format = format; // 压缩格式(如 AAC)
offloadInfo.sample_rate = sampleRateInHertz;
offloadInfo.channel_mask = nativeChannelMask;
offloadInfo.has_video = false;
offloadInfo.stream_type = AUDIO_STREAM_MUSIC; //required for offload // offload 要求
}
if (encapsulationMode != 0) {
// 类似填充,另加 encapsulation_mode、content_id、sync_id(来自 tunerHelper)
offloadInfo = AUDIO_INFO_INITIALIZER;
offloadInfo.format = format;
offloadInfo.sample_rate = sampleRateInHertz;
offloadInfo.channel_mask = nativeChannelMask;
offloadInfo.stream_type = AUDIO_STREAM_MUSIC;
offloadInfo.encapsulation_mode =
static_cast<audio_encapsulation_mode_t>(encapsulationMode);
offloadInfo.content_id = tunerHelper.getContentId();
offloadInfo.sync_id = tunerHelper.getSyncId();
}
// offload(硬件卸载):把解码工作交给 DSP 等硬件,App/CPU 只喂压缩数据。AUDIO_STREAM_MUSIC 是 offload 的硬性要求。encapsulationMode 分支是 Android 12 的 tuner/封装场景,复用同一个结构。
// initialize the native AudioTrack object
// 真正初始化 native AudioTrack(重点!)
// TRANSFER 三种模式 传输方式
// TRANSFER_SYNC:普通流式,write() 同步把数据灌给 native(你笔记里 MODE_STREAM 的数据通道)
// TRANSFER_SYNC_NOTIF_CALLBACK:offload 模式,配合回调通知
// TRANSFER_SHARED:静态模式,通过共享内存(lpJniStorage->mMemBase)传数据------对应你笔记里"静态模式一次性 loadStaticData"
// audioCallback + mCallbackData native AudioTrack 以回调模式初始化 audioCallback 是 C 静态函数,会被native线程调用 比如 数据请求、位置事件,他手里又 mCallbackData(cookie),能找回 JNI storage → 找到 Java 对象 → 调 postEventFromNative
// thread can call Java = true:允许回调线程进入 JVM(需要 AttachCurrentThread),这是"native 线程里调 Java 方法"的前提
// offload ? 0 : frameCount:offload 模式下缓冲大小由硬件决定,传 0 让 native 自己算。
// goto native_init_failure:老式 C 风格错误处理------多个错误点汇聚到一个统一的清理出口,避免重复写释放代码。注意 goto 在这里是向下跳转的合法用法。
status_t status = NO_ERROR;
switch (memoryMode) {
case MODE_STREAM:
status = lpTrack->set(AUDIO_STREAM_DEFAULT, // stream type, but more info conveyed
// in paa (last argument)
sampleRateInHertz,
format, // word length, PCM
nativeChannelMask, offload ? 0 : frameCount, // offload 时 frameCount 用 0
offload ? AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD
: AUDIO_OUTPUT_FLAG_NONE, // 输出标志
audioCallback, // ← 静态回调函数!
&(lpJniStorage->mCallbackData), // callback, callback data (user) // 回调数据(带 cookie)
0, // notificationFrames == 0 since not using EVENT_MORE_DATA // notificationFrames:不用 EVENT_MORE_DATA
// to feed the AudioTrack
0, // shared mem // shared mem:流式不需要(走 write)
true, // thread can call Java // thread can call Java:回调线程允许进 JVM
sessionId, // audio session ID
offload ? AudioTrack::TRANSFER_SYNC_NOTIF_CALLBACK
: AudioTrack::TRANSFER_SYNC, // 传输模式
(offload || encapsulationMode) ? &offloadInfo : NULL,
AttributionSourceState(), // default uid, pid values // 默认 uid/pid(后面 set 内部会填)
paa.get());
break;
case MODE_STATIC:
// AudioTrack is using shared memory
if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");
goto native_init_failure;
}
status = lpTrack->set(AUDIO_STREAM_DEFAULT, // stream type, but more info conveyed
// in paa (last argument)
sampleRateInHertz,
format, // word length, PCM
nativeChannelMask, frameCount, AUDIO_OUTPUT_FLAG_NONE,
audioCallback,
&(lpJniStorage->mCallbackData), // callback, callback data (user)
0, // notificationFrames == 0 since not using EVENT_MORE_DATA
// to feed the AudioTrack
lpJniStorage->mMemBase, // shared mem // ← 共享内存!
true, // thread can call Java
sessionId, // audio session ID
AudioTrack::TRANSFER_SHARED, // 共享传输
NULL, // default offloadInfo
AttributionSourceState(), // default uid, pid values
paa.get());
break;
default:
ALOGE("Unknown mode %d", memoryMode);
goto native_init_failure;
}
if (status != NO_ERROR) {
ALOGE("Error %d initializing AudioTrack", status);
goto native_init_failure;
}
// Set caller name so it can be logged in destructor.
// MediaMetricsConstants.h: AMEDIAMETRICS_PROP_CALLERNAME_VALUE_JAVA
lpTrack->setCallerName("java");
} else { // end if (nativeAudioTrack == 0)
lpTrack = (AudioTrack*)nativeAudioTrack;
// TODO: We need to find out which members of the Java AudioTrack might
// need to be initialized from the Native AudioTrack
// these are directly returned from getters:
// mSampleRate
// mAudioFormat
// mStreamType
// mChannelConfiguration
// mChannelCount
// mState (?)
// mPlayState (?)
// these may be used internally (Java AudioTrack.audioParamCheck():
// mChannelMask
// mChannelIndexMask
// mDataLoadMode
// initialize the callback information:
// this data will be passed with every AudioTrack callback
lpJniStorage = new AudioTrackJniStorage();
lpJniStorage->mCallbackData.audioTrack_class = (jclass)env->NewGlobalRef(clazz);
// we use a weak reference so the AudioTrack object can be garbage collected.
lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this);
lpJniStorage->mCallbackData.busy = false;
}
// 共同收尾段
// 回调接线:JNIAudioTrackCallback 构造时传入缓存的 postNativeEventInJava(jmethodID)
// 两个 int[] 出参回填:session 用 GetPrimitiveArrayCritical 直写,sampleRate 用 SetIntArrayRegion
// long 句柄三件套:
// setAudioTrack(...) → SetLongField nativeTrackInJavaObj(C++ 对象指针,"桥桩"①)
// SetLongField jniData(JNI storage 指针,"桥桩"②)
// SetIntField fieldStreamType(把 native 推导出的真实 stream type 写回 Java 的 mStreamType)
// SetIntField 是最妙的闭环:回想你贴的 Java 构造器里 mStreamType = AudioSystem.STREAM_DEFAULT;------Java 先放个默认值,native 因为传了 AudioAttributes 推导出真实 stream type,再写回 Java。这就是"Java ↔ native 状态同步"的完整示例
// cookie 注册表:sAudioTrackCallBackCookies(全局)+ Mutex::Autolock(锁)。跨线程(回调线程/释放线程)查找或清理 JNI storage 用,Autolock 是 RAII 锁的范例。
lpJniStorage->mAudioTrackCallback =
new JNIAudioTrackCallback(env, thiz, lpJniStorage->mCallbackData.audioTrack_ref,
javaAudioTrackFields.postNativeEventInJava); // ← 缓存的 jmethodID
lpTrack->setAudioTrackCallback(lpJniStorage->mAudioTrackCallback); // 注册到 native track
nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL);
if (nSession == NULL) {
ALOGE("Error creating AudioTrack: Error retrieving session id pointer");
goto native_init_failure;
}
// read the audio session ID back from AudioTrack in case we create a new session
// 回写 session:GetPrimitiveArrayCritical 直接改 nSession[0]
nSession[0] = lpTrack->getSessionId();
env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
nSession = NULL;
{
// 回写 sampleRate:SetIntArrayRegion
const jint elements[1] = { (jint) lpTrack->getSampleRate() };
env->SetIntArrayRegion(jSampleRate, 0, 1, elements);
}
{ // scope for the lock
// 全局 cookie 注册表(锁保护)
Mutex::Autolock l(sLock);
sAudioTrackCallBackCookies.add(&lpJniStorage->mCallbackData);
}
// save our newly created C++ AudioTrack in the "nativeTrackInJavaObj" field
// of the Java object (in mNativeTrackInJavaObj)
setAudioTrack(env, thiz, lpTrack); // ① SetLongField nativeTrackInJavaObj = C++ 指针
// save the JNI resources so we can free them later
//ALOGV("storing lpJniStorage: %x\n", (long)lpJniStorage);
env->SetLongField(thiz, javaAudioTrackFields.jniData, (jlong)lpJniStorage); // ② SetLongField jniData
// since we had audio attributes, the stream type was derived from them during the
// creation of the native AudioTrack: push the same value to the Java object
env->SetIntField(thiz, javaAudioTrackFields.fieldStreamType, (jint) lpTrack->streamType()); // ③ SetIntField
return (jint) AUDIO_JAVA_SUCCESS;
// failures:
native_init_failure:
if (nSession != NULL) {
env->ReleasePrimitiveArrayCritical(jSession, nSession, 0);
}
env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_class);
env->DeleteGlobalRef(lpJniStorage->mCallbackData.audioTrack_ref);
delete lpJniStorage;
env->SetLongField(thiz, javaAudioTrackFields.jniData, 0);
// lpTrack goes out of scope, so reference count drops to zero
return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
}