Android13-Audio-AudioTrack-播放流程

Android 13 AudioTrack, AudioFlinger, AudioPolicy 完整交互流程

我将详细解析Android 13中AudioTrack、AudioFlinger和AudioPolicy三者之间的完整交互流程,涵盖从应用层到硬件层的所有关键步骤。

一、架构总览(Android 13)

复制代码
应用层 (AudioTrack API 33)
     ↓
Java AudioTrack (java/android/media/AudioTrack)
     ↓
JNI桥接 (android_media_AudioTrack.cpp)
     ↓
Native AudioTrack (libaudioclient.so)
     ↓
Binder IPC (IAudioTrack)
     ↓
AudioFlinger (services/audioflinger)
     ↓   ↓
     AudioPolicyService
     ↓
AudioPolicyManager
     ↓
Audio HAL (HIDL/AIDL 7.1)
     ↓
硬件驱动
     ↓
音频设备

二、详细调用流程

2.1 初始化阶段

Java层初始化

java 复制代码
// 应用层创建AudioTrack
AudioTrack audioTrack = new AudioTrack.Builder()
    .setAudioAttributes(new AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .setSpatializationBehavior(AudioAttributes.SPATIALIZATION_BEHAVIOR_AUTO)
        .setAllowedCapturePolicy(AudioAttributes.ALLOW_CAPTURE_BY_SYSTEM)
        .build())
    .setAudioFormat(new AudioFormat.Builder()
        .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
        .setSampleRate(48000)
        .setChannelMask(AudioFormat.CHANNEL_OUT_STEREO)
        .build())
    .setBufferSizeInBytes(bufferSize)
    .setPerformanceMode(AudioTrack.PERFORMANCE_MODE_LOW_LATENCY)
    .build();

Native AudioTrack初始化流程

cpp 复制代码
// frameworks/av/media/libaudioclient/AudioTrack.cpp
status_t AudioTrack::set(...) {
    // 1. 参数验证
    mAttributes = attr;
    mSampleRate = sampleRate;
    mFormat = format;
    mChannelMask = channelMask;
    
    // 2. Android 13新增:检查空间音频支持
    if (mAttributes.spatialization_behavior != AUDIO_SPATIALIZATION_BEHAVIOR_NEVER) {
        mSpatializerEnabled = checkSpatializationSupport();
    }
    
    // 3. 创建Track
    status_t status = createTrack_l();
    return status;
}

status_t AudioTrack::createTrack_l() {
    // 4. 通过AudioSystem获取输出
    audio_io_handle_t output = AUDIO_IO_HANDLE_NONE;
    sp<IAudioTrack> track = AudioSystem::getOutputForAttr(
        &mAttributes, &output, mSessionId, &mStreamType, mClientPid,
        mClientUid, &mConfig, mFlags, nullptr, mSelectedDeviceId,
        mPortId, mCallerName);
    
    if (track == 0) {
        return NO_INIT;
    }
    
    // 5. 建立IPC连接
    mAudioTrack = track;
    mCblkMemory = mAudioTrack->getCblk();
    
    if (mCblkMemory != 0) {
        mCblk = static_cast<audio_track_cblk_t*>(mCblkMemory->pointer());
        // 初始化共享内存
    }
    
    return NO_ERROR;
}

2.2 AudioPolicy决策流程

AudioPolicyService交互

cpp 复制代码
// frameworks/av/services/audiopolicy/service/AudioPolicyService.cpp
sp<IAudioTrack> AudioPolicyService::getOutputForAttr(...) {
    // 1. 验证客户端权限
    if (!isAudioServerOrMediaServerUid(callingUid)) {
        if (!checkRecording(attr, clientPid, clientUid)) {
            return 0;
        }
    }
    
    // 2. 调用AudioPolicyManager进行决策
    status_t result = mAudioPolicyManager->getOutputForAttr(attr, output,
                                                           sessionId, stream,
                                                           clientUid, config,
                                                           flags, selectedDeviceId,
                                                           portId, callerName);
    
    if (result != NO_ERROR) {
        return 0;
    }
    
    return track;
}

AudioPolicyManager决策逻辑

cpp 复制代码
// frameworks/av/services/audiopolicy/common/managerdefinitions/AudioPolicyManager.cpp
status_t AudioPolicyManager::getOutputForAttr(...) {
    // 1. 选择输出设备
    DeviceVector outputDevices = getNewOutputDevices(stream, isSpatialized);
    
    // 2. Android 13新增:检查空间音频设备
    if (attr->spatialization_behavior != AUDIO_SPATIALIZATION_BEHAVIOR_NEVER) {
        if (isSpatializationSupported(selectedDeviceId)) {
            flags |= AUDIO_OUTPUT_FLAG_SPATIALIZER;
        }
    }
    
    // 3. 检查低延迟支持
    if ((flags & AUDIO_OUTPUT_FLAG_FAST) != 0) {
        if (!isFastOutputSupported(output, config, devices)) {
            flags &= ~AUDIO_OUTPUT_FLAG_FAST;
        }
    }
    
    // 4. 获取或创建输出描述符
    sp<SwAudioOutputDescriptor> outputDesc = getOutputForDevice(
        devices, sessionId, config, flags, isSpatialized);
    
    if (outputDesc == 0) {
        // 5. 需要打开新的输出
        audio_io_handle_t newOutput = openOutput(devices, config, flags, outputDesc);
        if (newOutput == AUDIO_IO_HANDLE_NONE) {
            return NO_INIT;
        }
        output = newOutput;
    } else {
        output = outputDesc->mIoHandle;
    }
    
    // 6. 更新路由
    setOutputDevices(outputDesc, devices, true, 0);
    
    return NO_ERROR;
}

2.3 AudioFlinger创建Track

AudioFlinger接收请求

cpp 复制代码
// frameworks/av/services/audioflinger/AudioFlinger.cpp
sp<IAudioTrack> AudioFlinger::createTrack(...) {
    sp<PlaybackThread::Track> track;
    sp<TrackHandle> trackHandle;
    
    // 1. 验证客户端权限
    if (!isTrustedCaller(clientPid, clientUid)) {
        ALOGE("Untrusted client");
        return 0;
    }
    
    // 2. 获取或创建PlaybackThread
    sp<PlaybackThread> thread = getPlaybackThread_l(output);
    if (thread == 0) {
        // 创建新的线程
        thread = openOutput_l(output, config, flags, devices);
    }
    
    // 3. 在PlaybackThread中创建Track
    track = thread->createTrack_l(client, streamType, attr, &clientConfig,
                                  &frameCount, &notificationFrames,
                                  sharedBuffer, sessionId, flags,
                                  clientDescriptor, clientUid, &policyStatus,
                                  portId);
    
    if (track != 0) {
        // 4. 创建TrackHandle(Binder接口)
        trackHandle = new TrackHandle(track);
        
        // 5. Android 13新增:注册音频会话
        registerAudioSession_l(sessionId, clientUid, clientPid);
    }
    
    return trackHandle;
}

PlaybackThread创建Track

cpp 复制代码
// frameworks/av/services/audioflinger/Threads.cpp
sp<AudioFlinger::PlaybackThread::Track> 
AudioFlinger::PlaybackThread::createTrack_l(...) {
    sp<Track> track;
    
    // 1. 创建Track对象
    track = new Track(this, client, streamType, attr, clientConfig, 
                     sampleRate, format, channelMask, frameCount,
                     sharedBuffer, sessionId, flags, clientDescriptor);
    
    // 2. 初始化Track
    track->initTrack();
    
    // 3. 添加到活跃轨道列表
    mActiveTracks.add(track);
    
    // 4. 更新音频策略
    mpAudioPolicy->registerClient(clientDescriptor, clientUid, sessionId);
    
    // 5. 唤醒混音线程
    broadcast_l();
    
    return track;
}

2.4 数据写入流程

AudioTrack写数据

cpp 复制代码
// frameworks/av/media/libaudioclient/AudioTrack.cpp
ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking) {
    // 1. 获取帧数
    size_t frameSize = mFrameSize;
    size_t framesReq = userSize / frameSize;
    
    // 2. 计算可用空间
    uint32_t framesAvail = 0;
    uint32_t u = mCblk->user;
    uint32_t s = mCblk->server;
    
    if (u >= s) {
        framesAvail = mCblk->frameCount - u + s;
    } else {
        framesAvail = s - u;
    }
    
    // 3. Android 13新增:快速路径检查
    if (framesReq <= framesAvail) {
        // 直接写入
        memcpy(mCblk->buffer(u), buffer, framesReq * frameSize);
        mCblk->stepUser(framesReq);
        
        // 通知AudioFlinger
        mAudioTrack->signal();
        
        return framesReq * frameSize;
    }
    
    // 4. 阻塞等待
    if (blocking) {
        mCv.wait(mLock);
        return write(buffer, userSize, true);
    }
    
    return 0;
}

AudioFlinger混音循环

cpp 复制代码
// frameworks/av/services/audioflinger/Threads.cpp
bool AudioFlinger::PlaybackThread::threadLoop() {
    while (!exitPending()) {
        // 1. 等待工作信号
        mWaitWorkCV.wait(mLock);
        
        // 2. 准备混音
        Vector< sp<Track> > tracksToRemove;
        { // 临界区
            Mutex::Autolock _l(mLock);
            prepareTracks_l(&tracksToRemove);
        }
        
        // 3. 执行混音
        mAudioMixer->process();
        
        // 4. Android 13新增:空间音频处理
        if (mSpatializerEnabled) {
            processSpatialization();
        }
        
        // 5. 写入硬件
        ssize_t bytesWritten = write_l();
        
        // 6. 清理完成的Track
        if (!tracksToRemove.isEmpty()) {
            removeTracks_l(tracksToRemove);
        }
    }
    return false;
}

2.5 共享内存机制(Android 13优化)

cpp 复制代码
// 共享内存控制块(优化版本)
struct audio_track_cblk_t {
    // 同步原语
    android::fence atomic mLock;
    
    // 环形缓冲区
    uint32_t    user;           // 生产者指针
    uint32_t    server;         // 消费者指针
    uint32_t    frameCount;     // 总帧数
    
    // Android 13新增:性能监控字段
    uint32_t    underrunCount;  // 欠载次数
    uint64_t    totalFrames;    // 总帧数
    uint32_t    latencyMs;      // 延迟
    
    // 缓冲区状态
    uint32_t    flags;          // 状态标志
    uint32_t    level[2];       // 填充水平
    
    // 获取缓冲区指针
    void* buffer(uint32_t offset) const {
        return static_cast<char*>(this + 1) + offset * mFrameSize;
    }
};

三、关键IPC调用序列

3.1 完整Binder调用序列

复制代码
时间轴 调用者                         被调用者                         方法
══════════════════════════════════════════════════════════════════════════
T0:    Application → AudioTrack        JNI             native_setup()
T1:    JNI → Native AudioTrack         AudioSystem     getOutputForAttr()
T2:    AudioSystem → AudioFlinger      IAudioFlinger   createTrack()
T3:    AudioFlinger → AudioPolicyService IAudioPolicy getOutputForAttr()
T4:    AudioPolicyService → AudioPolicyManager getOutputForAttr()
T5:    AudioFlinger → PlaybackThread   createTrack_l()
T6:    AudioTrack → AudioFlinger       IAudioTrack     start()
T7:    AudioTrack → AudioFlinger       IAudioTrack     write() [共享内存]
T8:    PlaybackThread → AudioHAL       IStreamOut      write()
T9:    AudioHAL → 硬件驱动             硬件接口         DMA传输
══════════════════════════════════════════════════════════════════════════

3.2 IPC接口定义

IAudioTrack接口

cpp 复制代码
// frameworks/av/media/libaudioclient/include/media/IAudioTrack.h
class IAudioTrack : public IInterface {
public:
    DECLARE_META_INTERFACE(AudioTrack);
    
    // 核心方法
    virtual status_t    start() = 0;
    virtual void        stop() = 0;
    virtual void        flush() = 0;
    virtual void        pause() = 0;
    
    // 获取共享内存
    virtual sp<IMemory> getCblk() const = 0;
    
    // Android 13新增
    virtual status_t    signal() = 0;  // 通知有数据
    virtual uint32_t    getLatency() = 0;
    virtual status_t    setVolume(float left, float right) = 0;
    
    // 空间音频支持
    virtual status_t    setSpatializationParameters(
                            float azimuth, float elevation, float distance) = 0;
};

IAudioFlinger接口

cpp 复制代码
// frameworks/av/services/audioflinger/AudioFlinger.h
class IAudioFlinger : public IInterface {
public:
    // 创建Track
    virtual sp<IAudioTrack> createTrack(
                        const CreateTrackInput& input,
                        CreateTrackOutput& output) = 0;
    
    // 打开/关闭输出
    virtual audio_io_handle_t openOutput(audio_module_handle_t module,
                                        audio_config_t* config,
                                        audio_output_flags_t flags) = 0;
    virtual status_t closeOutput(audio_io_handle_t output) = 0;
    
    // Android 13新增
    virtual status_t getAudioHwSyncForSession(audio_session_t sessionId,
                                            audio_hw_sync_t* hwSync) = 0;
    
    // 低功耗音频
    virtual status_t setLowPowerAudio(bool enabled) = 0;
};

四、Android 13新增功能实现

4.1 空间音频支持

AudioPolicyManager空间音频处理

cpp 复制代码
// frameworks/av/services/audiopolicy/common/managerdefinitions/AudioPolicyManager.cpp
bool AudioPolicyManager::isSpatializationSupported(
    audio_port_handle_t deviceId) {
    
    // 1. 获取设备描述符
    sp<DeviceDescriptor> deviceDesc = mAvailableOutputDevices.getDeviceFromId(deviceId);
    if (deviceDesc == nullptr) {
        return false;
    }
    
    // 2. 检查设备类型
    if (!deviceDesc->hasDynamicChannels()) {
        return false;
    }
    
    // 3. 检查HAL支持
    sp<SwAudioOutputDescriptor> outputDesc = getOutputForDevice(deviceId);
    if (outputDesc == nullptr) {
        return false;
    }
    
    // 4. 查询HAL
    audio_hw_device_t* hwDevice = outputDesc->mProfile->getAudioHwDevice();
    if (hwDevice->is_spatialization_supported == nullptr) {
        return false;
    }
    
    return hwDevice->is_spatialization_supported(hwDevice);
}

AudioFlinger空间音频处理

cpp 复制代码
// frameworks/av/services/audioflinger/AudioFlinger.cpp
void AudioFlinger::PlaybackThread::processSpatialization() {
    if (!mSpatializerEnabled) {
        return;
    }
    
    // 1. 获取空间化器
    sp<ISpatializer> spatializer = mSpatializer.promote();
    if (spatializer == nullptr) {
        // 创建空间化器
        spatializer = Spatializer::create();
        mSpatializer = spatializer;
    }
    
    // 2. 处理每个轨道
    for (size_t i = 0; i < mActiveTracks.size(); i++) {
        sp<Track> track = mActiveTracks[i];
        
        if (track->isSpatialized()) {
            // 3. 获取空间参数
            SpatializationParams params = track->getSpatializationParams();
            
            // 4. 应用空间化处理
            spatializer->process(track->mainBuffer(),
                               track->auxBuffer(),
                               track->frameCount(),
                               params);
        }
    }
}

4.2 低功耗音频

电源管理集成

cpp 复制代码
// frameworks/av/services/audioflinger/AudioFlinger.cpp
status_t AudioFlinger::setLowPowerAudio(bool enabled) {
    Mutex::Autolock _l(mLock);
    
    // 1. 更新全局状态
    mLowPowerAudioEnabled = enabled;
    
    // 2. 通知所有线程
    for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
        mPlaybackThreads.valueAt(i)->setLowPowerAudio(enabled);
    }
    
    // 3. 更新HAL
    for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
        audio_hw_device_t* dev = mAudioHwDevs.valueAt(i)->hwDevice();
        if (dev->set_low_power_audio != nullptr) {
            dev->set_low_power_audio(dev, enabled);
        }
    }
    
    return NO_ERROR;
}

PlaybackThread低功耗处理

cpp 复制代码
void AudioFlinger::PlaybackThread::setLowPowerAudio(bool enabled) {
    mIsLowPower = enabled;
    
    if (enabled) {
        // 1. 降低采样率
        if (mSampleRate > 24000) {
            mSampleRate = 24000;
        }
        
        // 2. 减少缓冲区大小
        mNormalFrameCount = mFrameCount;
        mFrameCount = mFrameCount / 2;
        
        // 3. 降低混合质量
        mAudioMixer->setLowPowerMode(true);
        
        // 4. 延长休眠时间
        mMaxSleepTime = 20 * 1000;  // 20ms
    } else {
        // 恢复设置
        mFrameCount = mNormalFrameCount;
        mAudioMixer->setLowPowerMode(false);
        mMaxSleepTime = 5 * 1000;  // 5ms
    }
}

五、错误处理和恢复机制

5.1 音频服务崩溃恢复

cpp 复制代码
// frameworks/av/media/libaudioclient/AudioTrack.cpp
status_t AudioTrack::restoreTrack_l(const char* from) {
    // 1. 检查是否需要恢复
    if (mAudioTrack != nullptr && mAudioTrack->asBinder()->isBinderAlive()) {
        return NO_ERROR;
    }
    
    // 2. 保存当前状态
    uint32_t position = mCblk->server;
    float volumeLeft, volumeRight;
    getVolume(&volumeLeft, &volumeRight);
    
    // 3. 重新创建Track
    status_t status = createTrack_l();
    if (status != NO_ERROR) {
        ALOGE("restoreTrack_l() failed status=%d", status);
        return status;
    }
    
    // 4. 恢复状态
    mCblk->server = position;
    setVolume(volumeLeft, volumeRight);
    
    // 5. 如果之前是播放状态,重新开始
    if (mActive) {
        status = mAudioTrack->start();
    }
    
    return status;
}

5.2 欠载处理

cpp 复制代码
// frameworks/av/services/audioflinger/Threads.cpp
void AudioFlinger::PlaybackThread::handleUnderrun() {
    mUnderrunCount++;
    
    if (mUnderrunCount > MAX_UNDERRUN_COUNT) {
        // 1. 记录错误
        ALOGW("Excessive underrun, count=%d", mUnderrunCount);
        
        // 2. 尝试恢复
        if (mOutput != nullptr) {
            // 停止输出
            mOutput->standby();
            
            // 重新开始
            mOutput->getRenderPosition(&mRenderPosition);
            
            // 清空缓冲区
            memset(mMixBuffer, 0, mMixBufferSize);
            
            // 重新写入静音
            mOutput->write(mMixBuffer, mFrameCount * mFrameSize);
        }
        
        // 3. 重置计数器
        mUnderrunCount = 0;
        
        // 4. 通知客户端
        for (size_t i = 0; i < mActiveTracks.size(); i++) {
            mActiveTracks[i]->notifyUnderrun();
        }
    }
}

六、性能监控和调试

6.1 性能指标收集

cpp 复制代码
// frameworks/av/services/audioflinger/AudioFlinger.cpp
void AudioFlinger::collectPerformanceMetrics() {
    // Android 13新增的性能监控
    struct PerformanceMetrics {
        uint64_t totalFramesMixed;
        uint64_t totalUnderruns;
        uint32_t averageLatencyMs;
        uint32_t peakLatencyMs;
        float cpuUsagePercent;
        float memoryUsageMB;
    } metrics = {};
    
    // 收集所有线程的指标
    for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
        sp<PlaybackThread> thread = mPlaybackThreads.valueAt(i);
        
        metrics.totalFramesMixed += thread->getFramesMixed();
        metrics.totalUnderruns += thread->getUnderrunCount();
        metrics.averageLatencyMs += thread->getLatencyMs();
        metrics.peakLatencyMs = max(metrics.peakLatencyMs, 
                                   thread->getPeakLatencyMs());
    }
    
    // 计算CPU使用率
    struct rusage usage;
    getrusage(RUSAGE_SELF, &usage);
    metrics.cpuUsagePercent = (usage.ru_utime.tv_sec + usage.ru_stime.tv_sec) * 100.0f;
    
    // 记录到系统属性(用于监控)
    char buffer[256];
    snprintf(buffer, sizeof(buffer),
             "frames:%llu underruns:%llu latency:%ums cpu:%.1f%%",
             metrics.totalFramesMixed, metrics.totalUnderruns,
             metrics.averageLatencyMs, metrics.cpuUsagePercent);
    property_set("sys.audio.metrics", buffer);
}

6.2 调试命令

bash 复制代码
# Android 13新增的调试命令
# 1. 音频性能监控
adb shell dumpsys media.audio_flinger --perf
adb shell dumpsys media.audio_policy --stats

# 2. 空间音频状态
adb shell dumpsys audio spatializer

# 3. 低功耗音频状态
adb shell dumpsys audio power

# 4. 音频会话管理
adb shell dumpsys media.audio_session --verbose

# 5. 实时监控
adb shell top -p $(pidof audioserver)
adb shell cat /proc/$(pidof audioserver)/status

# 6. 跟踪音频调用
adb shell atrace --async_start audio -c -b 16384

七、安全增强

7.1 权限验证

cpp 复制代码
// frameworks/av/services/audioflinger/AudioFlinger.cpp
bool AudioFlinger::isTrustedCaller(pid_t pid, uid_t uid) {
    // 1. 系统进程
    if (uid == AID_MEDIA || uid == AID_AUDIOSERVER) {
        return true;
    }
    
    // 2. 检查SELinux上下文
    char* seContext = nullptr;
    if (getpidcon(pid, &seContext) >= 0) {
        bool trusted = strstr(seContext, "u:r:untrusted_app") == nullptr;
        freecon(seContext);
        return trusted;
    }
    
    // 3. 检查应用ID
    if (uid >= AID_APP_START && uid <= AID_APP_END) {
        // 普通应用,需要检查权限
        return checkPermission(pid, uid);
    }
    
    return false;
}

bool AudioFlinger::checkPermission(pid_t pid, uid_t uid) {
    // Android 13新增:运行时权限检查
    const char* permission = nullptr;
    
    if (mIsRecording) {
        permission = "android.permission.RECORD_AUDIO";
    } else {
        permission = "android.permission.MODIFY_AUDIO_SETTINGS";
    }
    
    // 通过PermissionChecker检查
    PermissionChecker permissionChecker;
    return permissionChecker.checkPermission(permission, pid, uid);
}

八、完整示例:三方通话场景

cpp 复制代码
// 三方通话场景中的音频路由
status_t AudioPolicyManager::handleThreeWayCall(audio_session_t session1,
                                               audio_session_t session2,
                                               audio_session_t session3) {
    // 1. 获取三个会话的输出描述符
    sp<SwAudioOutputDescriptor> output1 = getOutputForSession(session1);
    sp<SwAudioOutputDescriptor> output2 = getOutputForSession(session2);
    sp<SwAudioOutputDescriptor> output3 = getOutputForSession(session3);
    
    // 2. 检查是否需要合并输出
    if (output1 != output2 || output2 != output3) {
        // 需要重新路由
        
        // 3. 选择最佳输出设备
        DeviceVector devices = getNewOutputDevices(AUDIO_STREAM_VOICE_CALL, false);
        
        // 4. Android 13新增:检查通话连续性
        if (isCallContinuityRequired(session1, session2, session3)) {
            // 确保通话不中断
            setOutputDevices(output1, devices, true, 0);
            
            // 将其他会话路由到同一输出
            rerouteSessionToOutput(session2, output1->mIoHandle);
            rerouteSessionToOutput(session3, output1->mIoHandle);
        } else {
            // 分别路由
            setOutputDevices(output1, devices, true, 0);
            setOutputDevices(output2, devices, true, 0);
            setOutputDevices(output3, devices, true, 0);
        }
    }
    
    // 5. 设置音量
    float volume = computeConferenceVolume(output1, output2, output3);
    output1->setVolume(volume, volume);
    
    return NO_ERROR;
}

总结

Android 13中AudioTrack、AudioFlinger、AudioPolicy的交互流程在以下方面得到增强:

  1. 空间音频支持:完整的端到端空间音频处理链路
  2. 低功耗优化:智能的电源管理和性能调节
  3. 安全性增强:严格的权限验证和隐私保护
  4. 性能监控:详细的性能指标收集和报告
  5. 稳定性改进:更好的错误恢复和容错机制
  6. API增强:更丰富的控制接口和配置选项

整个系统通过精细的分层设计和高效的IPC机制,提供了高性能、低延迟、高可靠的音频播放体验。

相关推荐
Yutengii2 小时前
b站视频下载到电脑本地的方法有哪些
音视频
モンキー・D・小菜鸡儿3 小时前
Android 系统TTS(文字转语音)解析
android·tts
2501_915909063 小时前
iOS 反编译防护工具全景解析 从底层符号到资源层的多维安全体系
android·安全·ios·小程序·uni-app·iphone·webview
Swizard3 小时前
速度与激情:Android Python + CameraX 零拷贝实时推理指南
android·python·ai·移动开发
里纽斯4 小时前
RK平台Watchdog硬件看门狗验证
android·linux·rk3588·watchdog·看门狗·rk平台·wtd
三七吃山漆4 小时前
攻防世界——comment
android·python·web安全·网络安全·ctf
用户413079810614 小时前
终于懂了-ARouter原理初探
android
fatiaozhang95275 小时前
晶晨S905L3B芯片-2+8G-安卓9.0-ATV原生设置(深度精简优化)-通刷-线刷固件包
android·电视盒子·刷机固件·机顶盒刷机·晶晨s905l3b通刷包·e900v22c-s905l3
Black蜡笔小新5 小时前
安防监控/录像存储EasyCVR视频汇聚平台无法启动的原因排查
音视频