android_media_AudioTrack_setup

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;
}
相关推荐
Neighbor_OldY1 小时前
【实战复盘】文件上传漏洞检测与应急处置:校验绕过、图片马与WebShell的排查修复指南
运维·前端·web安全
晓天衡宇•评测社区1 小时前
Seedance 2.5 登顶图生视频榜单,Wan 3.0 在游戏与教育场景进入前列
前端·javascript·网络
码海无涯回头无岸1 小时前
Ai agent - LLM是一个函数
前端
Profile排查笔记1 小时前
指纹浏览器推荐:用一套验收清单筛选 Profile、代理与自动化能力
前端·人工智能·后端·自动化
卡皮巴拉c991 小时前
基于pnpm搭建monorepo项目
前端·javascript
yume_sibai2 小时前
CSS2 核心知识点完全总结(选择器 + 三大特性 + 常用属性)
前端·css
掘金者阿豪2 小时前
不装 SSH 客户端也能连服务器:用 WebSSH 把终端搬进浏览器
前端
Bug修理工Bubble2 小时前
Optional:为什么一个 String? 能让代码少掉很多崩溃?
前端
巴勒个啦2 小时前
2026 年 CSS 选型真相:我用 Tailwind v4 + 原生新特性重构了一个组件库
前端·angular.js