OpenSL ES 学习

OpenSL ES 学习

播放mp3、ogg、wav等

头文件

//
// Created by 29051 on 2025/9/20.
//

#ifndef OPENSL_LEARN_OPENSL_LEARN_HPP
#define OPENSL_LEARN_OPENSL_LEARN_HPP

#include <thread>
#include <chrono>
#include <format>

#include <jni.h>
#include <android/looper.h>

#include "learn001.hpp"

extern "C" {
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
}

extern "C"
JNIEXPORT void JNICALL
Java_io_github_opensllearn_utils_Utils_hello(JNIEnv *env, jobject thiz, jint fd);

#endif // OPENSL_LEARN_OPENSL_LEARN_HPP

源文件

#include "openslLearn.hpp"

const char* const TAG = "openslLearn";

void openSLESPlay(const int fd){

    logger::info(TAG, "fd: %d", fd);

    // 暂时使用GOTO替代RAII
    SLObjectItf engineObject = nullptr;
    SLEngineItf engineEngine = nullptr;
    SLObjectItf outputMixObject = nullptr;
    SLObjectItf playerObject = nullptr;
    SLAndroidConfigurationItf playerConfig = nullptr;
    SLPlayItf playerPlay = nullptr;
    SLPrefetchStatusItf prefetchStatusItf = nullptr;

    SLDataLocator_OutputMix locatorOutputMix;
    SLDataSink dataSink;
    SLDataLocator_AndroidFD locatorAndroidFd;
    SLDataFormat_MIME mime;
    SLDataSource  dataSource;

    SLuint32  performanceMode = SL_ANDROID_PERFORMANCE_LATENCY;


    constexpr int INTERFACE_COUNT = 3;

    const SLInterfaceID interfaces[INTERFACE_COUNT] = {
            SL_IID_PREFETCHSTATUS,
            SL_IID_EQUALIZER,
            SL_IID_ANDROIDCONFIGURATION,
    };

    const SLboolean required[INTERFACE_COUNT] = {
            SL_BOOLEAN_TRUE,
            SL_BOOLEAN_TRUE,
            SL_BOOLEAN_TRUE,
    };

    SLmillisecond duration = SL_TIME_UNKNOWN;
    SLuint32 prefetchStatus = SL_PREFETCHSTATUS_UNDERFLOW;

    int sleepStatus = 0;

    auto result = slCreateEngine(&engineObject, 0, nullptr, 0, nullptr, nullptr);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "引擎创建失败");
        return;
    }

    // 初始化引擎
    result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "引擎初始化失败");
        goto error;
    }

    result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "获取引擎失败");
        goto error;
    }

    result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, nullptr, nullptr);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "创建混音器失败");
        goto error;
    }

    result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "初始化混音器失败");
        goto error;
    }

    locatorOutputMix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
    locatorOutputMix.outputMix = outputMixObject;

    dataSink.pLocator = &locatorOutputMix;
    dataSink.pFormat = nullptr;

    locatorAndroidFd.fd = fd;
    locatorAndroidFd.locatorType = SL_DATALOCATOR_ANDROIDFD;
    locatorAndroidFd.length = SL_DATALOCATOR_ANDROIDFD_USE_FILE_SIZE;
    locatorAndroidFd.offset = 0LL;

    mime.formatType = SL_DATAFORMAT_MIME;
    mime.mimeType = nullptr;
    mime.containerType = SL_CONTAINERTYPE_UNSPECIFIED;

    dataSource.pFormat = &mime;
    dataSource.pLocator = &locatorAndroidFd;

    result = (*engineEngine)->CreateAudioPlayer(engineEngine, &playerObject, &dataSource, &dataSink, INTERFACE_COUNT, interfaces, required);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "创建音频播放器失败");
        goto error;
    }

    result = (*playerObject)->GetInterface(playerObject, SL_IID_ANDROIDCONFIGURATION, &playerConfig);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "获取播放配置失败!");
        goto error;
    }

    result = (*playerConfig)->SetConfiguration(playerConfig, SL_ANDROID_KEY_PERFORMANCE_MODE, &performanceMode,sizeof(SLuint32));
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "设置播放配置失败");
        goto error;
    }

    result = (*playerObject)->Realize(playerObject, SL_BOOLEAN_FALSE);

    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "初始化播放器失败");
        goto error;
    }

    result = (*playerObject)->GetInterface(playerObject, SL_IID_PLAY, &playerPlay);

    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "获取播放器失败");
        goto error;
    }

    result = (*playerObject)->GetInterface(playerObject, SL_IID_PREFETCHSTATUS, &prefetchStatusItf);

    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "获取preFetch失败");
        goto error;
    }

    result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PAUSED);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "播放暂停失败");
        goto error;
    }

    while(prefetchStatus != SL_PREFETCHSTATUS_SUFFICIENTDATA){
        sleepStatus = usleep(100 * 1000U);
        result = (*prefetchStatusItf)->GetPrefetchStatus(prefetchStatusItf, &prefetchStatus);
        logger::info(TAG, "sleepStatus: %d, result: %d", sleepStatus, result);
        if (result != SL_RESULT_SUCCESS){
            logger::error(TAG, "获取播放音频数据失败");
            goto error;
        }
    }

    result = (*playerPlay)->GetDuration(playerPlay, &duration);

    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "获取播放音频时长数据失败");
        goto error;
    }
    
    logger::info(TAG, "mp3音频时长为: %d", duration);
    if (duration == SL_TIME_UNKNOWN){
        duration = 3 * 1000; // 3 seconds
    }

    (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING);

    sleepStatus = usleep(duration * 1000); // 在ui线程会直接卡死UI
    logger::info(TAG, "sleepStatus: %d", sleepStatus);

    (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_STOPPED);
    logger::info(TAG, "播放完成!");
    goto error;

error:
    if (playerObject != nullptr){ // 顺序不对会奔溃
        (*playerObject)->Destroy(playerObject);
        playerObject = nullptr;
    }
    if (outputMixObject != nullptr){
        (*outputMixObject)->Destroy(outputMixObject);
        outputMixObject = nullptr;
    }
    if (engineObject != nullptr){
        (*engineObject)->Destroy(engineObject);
        engineObject = nullptr;
    }
}

extern "C"
JNIEXPORT void JNICALL
Java_io_github_opensllearn_utils_Utils_hello(JNIEnv *env, jobject thiz, jint fd) {
    openSLESPlay(fd);
}

封装为一个对象

头文件

//
// Created by 29051 on 2025/9/20.
//

#ifndef OPENSL_LEARN_OPENSL_LEARN_HPP
#define OPENSL_LEARN_OPENSL_LEARN_HPP

#include <thread>
#include <chrono>
#include <format>
#include <istream>

#include <jni.h>
#include <android/looper.h>

extern "C" {
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
}

#include "logging.hpp"

class OpenSLESPlayer {
private:
    int fd;
    SLObjectItf engineObject = nullptr;
    SLObjectItf outputMixObject = nullptr;
    SLObjectItf playerObject = nullptr;
    // 下面不需要自己回收
    SLPlayItf playerPlay = nullptr;
    SLSeekItf playerSeek = nullptr;
public:
    explicit OpenSLESPlayer(int fd);
    ~OpenSLESPlayer();
    bool play();
    bool pause();
    bool stop();
    bool seek(uint32_t position);
    uint32_t getDuration();
};

#endif // OPENSL_LEARN_OPENSL_LEARN_HPP

源文件

OpenSLESPlayer::OpenSLESPlayer(const int fd) : fd(fd) {
    if (fd < 0) {
        throw std::runtime_error("Invalid file descriptor");
    }
    // 创建引擎对象
    auto result = slCreateEngine(&engineObject, 0, nullptr, 0, nullptr, nullptr);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "引擎创建失败");
        throw std::runtime_error("Failed to create SL engine");
    }
    // 初始化引擎
    result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "引擎初始化失败");
        throw std::runtime_error("Failed to init SL engine");
    }
    // 获取 ENGINE 接口对象
    SLEngineItf engineEngine;
    result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "获取引擎失败");
        throw std::runtime_error("Failed to get SL engine");
    }
    // 创建混音器
    result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, nullptr, nullptr);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "创建混音器失败");
        throw std::runtime_error("Failed to create outputMixObject");
    }
    // 初始化混音器
    result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "初始化混音器失败");
        throw std::runtime_error("Failed to init outputMixObject");
    }

    // 设置输出混音器定位器 固定写法
    SLDataLocator_OutputMix locatorOutputMix;
    locatorOutputMix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
    locatorOutputMix.outputMix = outputMixObject;

    // 设置音频输出
    SLDataSink dataSink;
    dataSink.pLocator = &locatorOutputMix;
    dataSink.pFormat = nullptr;

    // 设置文件来源定位器
    SLDataLocator_AndroidFD locatorAndroidFd;
    // 文件资源描述符
    locatorAndroidFd.fd = fd;
    // 固定值,表示数据来源是文件
    locatorAndroidFd.locatorType = SL_DATALOCATOR_ANDROIDFD;
    // 自动计算文件大小
    locatorAndroidFd.length = SL_DATALOCATOR_ANDROIDFD_USE_FILE_SIZE;
    // 从文件开头读取
    locatorAndroidFd.offset = 0LL;


    SLDataFormat_MIME mime;
    // 固定值,表示使用 MIME 类型描述格式
    mime.formatType = SL_DATAFORMAT_MIME;
    // 自动推断
    mime.mimeType = nullptr;
    // 容器类型(未指定)
    mime.containerType = SL_CONTAINERTYPE_UNSPECIFIED;

    // 设置音频输入
    SLDataSource  dataSource;
    dataSource.pFormat = &mime;
    dataSource.pLocator = &locatorAndroidFd;

    // 请求三个扩展接口
    constexpr int INTERFACE_COUNT = 4;

    const SLInterfaceID interfaces[INTERFACE_COUNT] = {
            SL_IID_PREFETCHSTATUS, // 预加载状态接口
            SL_IID_EQUALIZER,      // 均衡器接口
            SL_IID_ANDROIDCONFIGURATION, // Android 专属配置接口
            SL_IID_SEEK, // Android 专属配置接口
    };

    // 是否必须
    const SLboolean required[INTERFACE_COUNT] = {
            SL_BOOLEAN_TRUE,
            SL_BOOLEAN_TRUE,
            SL_BOOLEAN_TRUE,
            SL_BOOLEAN_TRUE,
    };

    // 初始化播放器,并请求扩展接口
    result = (*engineEngine)->CreateAudioPlayer(engineEngine, &playerObject, &dataSource, &dataSink, INTERFACE_COUNT, interfaces, required);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "创建音频播放器失败");
        throw std::runtime_error("Failed to create playerObject");
    }

    // Android 专属配置接口
    SLAndroidConfigurationItf playerConfig;
    result = (*playerObject)->GetInterface(playerObject, SL_IID_ANDROIDCONFIGURATION, &playerConfig);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "获取播放配置失败!");
        throw std::runtime_error("Failed to get playerConfig");
    }

    // 设置低延迟性能模式
    SLuint32  performanceMode = SL_ANDROID_PERFORMANCE_LATENCY;
    result = (*playerConfig)->SetConfiguration(playerConfig, SL_ANDROID_KEY_PERFORMANCE_MODE, &performanceMode, sizeof(SLuint32));
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "设置播放配置失败");
        throw std::runtime_error("Failed to set playerConfig");
    }


    result = (*playerObject)->Realize(playerObject, SL_BOOLEAN_FALSE);

    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "初始化播放器失败");
        throw std::runtime_error("Failed init set playerObject");
    }

    result = (*playerObject)->GetInterface(playerObject, SL_IID_PLAY, &playerPlay);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "获取播放器失败");
        throw std::runtime_error("Failed init get playerPlay");
    }

    result = (*playerObject)->GetInterface(playerObject, SL_IID_SEEK, &playerSeek);

    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "获取定位器失败");
    }

    logger::info(TAG, "初始化完成");
}


bool OpenSLESPlayer::pause() {
    const auto result = (*this->playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PAUSED);
    return result == SL_RESULT_SUCCESS;
}

/**
 * stop 会清楚缓存, 下次从头开始播放
 * @return 是否stop成功
 */
bool OpenSLESPlayer::stop() {
    const auto result = (*this->playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_STOPPED);
    return result == SL_RESULT_SUCCESS;
}

bool OpenSLESPlayer::play() {
    SLuint32 currentState;
    auto result = (*this->playerPlay)->GetPlayState(playerPlay, &currentState);
    if(result != SL_RESULT_SUCCESS){
        return false;
    }
    logger::info(TAG, "当前播放状态: %d", currentState);
    if (currentState == SL_PLAYSTATE_PLAYING){
        return false;
    }
    result = (*this->playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING);
    return result == SL_RESULT_SUCCESS;
}

bool OpenSLESPlayer::seek(const uint32_t position) {
    if (playerSeek == nullptr){
        return false;
    }

    const auto duration = this->getDuration();
    if (position > duration){
        return false;
    }

    const auto result = (*playerSeek)->SetPosition(playerSeek, position, SL_SEEKMODE_ACCURATE);

    return result == SL_RESULT_SUCCESS;
}

uint32_t OpenSLESPlayer::getDuration() {

    SLmillisecond duration = SL_TIME_UNKNOWN;

    SLuint32 currentState;
    auto result = (*this->playerPlay)->GetPlayState(playerPlay, &currentState);
    if(result != SL_RESULT_SUCCESS){
        return duration;
    }

    logger::info(TAG, "当前播放状态: %d", currentState);

    if (currentState == SL_PLAYSTATE_STOPPED){
        (*this->playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PAUSED);
    }

    SLuint32 prefetchStatus = SL_PREFETCHSTATUS_UNDERFLOW;
    SLPrefetchStatusItf prefetchStatusItf;

    result = (*playerObject)->GetInterface(playerObject, SL_IID_PREFETCHSTATUS, &prefetchStatusItf);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "获取prefetchStatusItf失败");
        return duration;
    }

    while(prefetchStatus != SL_PREFETCHSTATUS_SUFFICIENTDATA){
        result = (*prefetchStatusItf)->GetPrefetchStatus(prefetchStatusItf, &prefetchStatus);
        logger::info(TAG, "while result: %d", result);
        if (result != SL_RESULT_SUCCESS){
            logger::error(TAG, "获取播放音频数据失败");
            break;
        }
    }

    result = (*playerPlay)->GetDuration(playerPlay, &duration);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "获取播放音频时长数据失败");
    }

    return duration;
}

OpenSLESPlayer::~OpenSLESPlayer() {
    if (this->playerObject != nullptr){
        (*playerObject)->Destroy(playerObject);
    }
    if (this->outputMixObject != nullptr){
        (*outputMixObject)->Destroy(outputMixObject);
    }
    if (this->engineObject != nullptr) {
        (*engineObject)->Destroy(engineObject);
    }
    close(fd);
    logger::info(TAG, "对象销毁完成");
}

extern "C"
JNIEXPORT jlong JNICALL
Java_io_github_opensllearn_utils_Utils_getPlayer(JNIEnv* env, jobject, jint fd) {
    OpenSLESPlayer *player = nullptr;
    try{
        player = new OpenSLESPlayer(fd);
    } catch (const std::exception &e) {
        delete player;
        player = nullptr;
        env->ThrowNew(env->FindClass("java/lang/RuntimeException"), e.what());
    }
    return reinterpret_cast<jlong>(player);
}

extern "C"
JNIEXPORT jboolean JNICALL
Java_io_github_opensllearn_utils_Utils_play(JNIEnv*, jobject, jlong ptr) {
    auto* player = reinterpret_cast<OpenSLESPlayer*>(ptr);
    return player->play();
}

extern "C"
JNIEXPORT jboolean JNICALL
Java_io_github_opensllearn_utils_Utils_pause(JNIEnv*, jobject, jlong ptr) {
    auto* player = reinterpret_cast<OpenSLESPlayer*>(ptr);
    return player->pause();
}

extern "C"
JNIEXPORT jboolean JNICALL
Java_io_github_opensllearn_utils_Utils_stop(JNIEnv*, jobject, jlong ptr) {
    auto* player = reinterpret_cast<OpenSLESPlayer*>(ptr);
    return player->stop();
}

extern "C"
JNIEXPORT jboolean JNICALL
Java_io_github_opensllearn_utils_Utils_seek(JNIEnv*, jobject, jlong ptr, jint position) {
    auto* player = reinterpret_cast<OpenSLESPlayer*>(ptr);
    return player->seek(position);
}

extern "C"
JNIEXPORT jint JNICALL
Java_io_github_opensllearn_utils_Utils_getDuration(JNIEnv*, jobject, jlong ptr) {
    auto* player = reinterpret_cast<OpenSLESPlayer*>(ptr);
    return static_cast<jint>(player->getDuration());
}

extern "C"
JNIEXPORT void JNICALL
Java_io_github_opensllearn_utils_Utils_releasePlayer(JNIEnv*, jobject, jlong ptr) {
    auto* player = reinterpret_cast<OpenSLESPlayer*>(ptr);
    delete player;
}

Jdk25 Hello World

import static java.lang.IO.println;
void main(){
	println("Hello World!");
}

运行:
java HelloWorld.java

播放PCM

//
// Created by 29051 on 2025/8/2.
//

#ifndef FFMPEGLEARN_OPENSLLEARN_HPP
#define FFMPEGLEARN_OPENSLLEARN_HPP
extern "C" {
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
}
#include <fstream>
#include <string>
#include <vector>
#include <memory>
#include <functional>

#include "logging.hpp"

struct PlaybackContext {
    std::unique_ptr<std::ifstream> pcmFile;
    std::unique_ptr<std::vector<char>> buffer;
    SLObjectItf playerObject;
    SLObjectItf engineObject;
    SLObjectItf outputMixObject;
    std::unique_ptr<std::function<void(SLAndroidSimpleBufferQueueItf)>> callback;
};

void playPcm(const std::string &pcmPath);
#endif //FFMPEGLEARN_OPENSLLEARN_HPP

源文件

//
// Created by 29051 on 2025/8/2.
//
#include "OpenSLLearn.hpp"
constexpr const char *TAG = "OpenSLLearn";

struct PlaybackContext {
    int minBufferSize;
    int fd;
    SLObjectItf playerObject;
    SLObjectItf engineObject;
    SLObjectItf outputMixObject;
    char *buffer;
};

// 桥接函数,OpenSL 会调用它
void bufferCallback(SLAndroidSimpleBufferQueueItf queue, void* context) {
    auto *ctx = static_cast<PlaybackContext*>(context);
    const auto size = read(ctx->fd, ctx->buffer, ctx->minBufferSize);
    logger::info(TAG, "bufferCallback size: %d, bufferSize: %d", size, sizeof(ctx->buffer));
    if (size > 0) {
        SLresult result = (*queue)->Enqueue(queue, ctx->buffer, size);
        if (result != SL_RESULT_SUCCESS){
            logger::error(TAG, "Enqueue failed: %d", result);
        }
    } else {
        logger::info(TAG, "播放结束...");
        close(ctx->fd);
        (*ctx->playerObject)->Destroy(ctx->playerObject);
        (*ctx->outputMixObject)->Destroy(ctx->outputMixObject);
        (*ctx->engineObject)->Destroy(ctx->engineObject);
        delete ctx->buffer;
        delete ctx;  // 一次释放所有资源
    }
}

/**
 * ffmpeg -i wav.wav -f s16le  -ac 2 -ar 48000 pcm.pcm
 * ffplay -f s16le -ch_layout stereo  -ar 48000 -i  pcm.pcm
 * 48000 Hz, 2 channels, s16
 * @param fd
 */
void openSLESPlayPCM(const int fd){

    logger::info(TAG, "openSLESPlayPCM fd: %d", fd);

    SLObjectItf engineObject = nullptr;
    SLObjectItf outputMixObject = nullptr;
    SLObjectItf playerObject = nullptr;

    SLEngineItf engineEngine = nullptr;
    SLAndroidConfigurationItf playerConfig = nullptr;
    SLPlayItf playerPlay = nullptr;
    SLAndroidSimpleBufferQueueItf bufferQueue = nullptr;

    PlaybackContext *playbackContext = nullptr;

    // 输出
    SLDataSink dataSink;
    // 输入
    SLDataSource  dataSource;

    constexpr int INTERFACE_COUNT = 2;

    const SLInterfaceID interfaces[INTERFACE_COUNT] = {
            SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
            SL_IID_ANDROIDCONFIGURATION,
    };

    const SLboolean required[INTERFACE_COUNT] = {
            SL_BOOLEAN_TRUE,
            SL_BOOLEAN_TRUE,
    };

    SLDataFormat_PCM formatPcm = {
            SL_DATAFORMAT_PCM,
            2,
            SL_SAMPLINGRATE_48,
            SL_PCMSAMPLEFORMAT_FIXED_16,
            SL_PCMSAMPLEFORMAT_FIXED_16,
            SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
            SL_BYTEORDER_LITTLEENDIAN
    };

    SLDataLocator_OutputMix locatorOutputMix;
    SLDataLocator_AndroidSimpleBufferQueue locatorAndroidBufferQueue = {
            .locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
            .numBuffers = 2,
    };

    SLuint32 performanceMode = SL_ANDROID_PERFORMANCE_LATENCY;

    constexpr int sampleRate = 48000;
    constexpr int channelCount = 2;
    constexpr int bitsPerSample = 16;

    // 20ms buffer
    constexpr int frameCount = sampleRate * 0.02;   // 960
    constexpr int frameSize = channelCount * (bitsPerSample / 8); // 4 bytes
    constexpr int minBufferSize = frameCount * frameSize;         // 3840 bytes

    // 创建引擎对象
    auto result = slCreateEngine(&engineObject, 0, nullptr, 0, nullptr, nullptr);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "引擎创建失败");
        return;
    }

    // 初始化引擎对象
    result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);

    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "引擎初始化失败");
        goto error;
    }

    // 获取引擎对象接口
    result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);

    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "获取创建失败");
        goto error;
    }

    // 创建混音器
    result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, nullptr, nullptr);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "创建混音器失败");
        goto error;
    }

    // 初始化混音器
    result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "初始化混音器失败");
        goto error;
    }

    // 输入
    dataSource.pLocator = &locatorAndroidBufferQueue;
    dataSource.pFormat = &formatPcm;

    // 固定写法
    locatorOutputMix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
    locatorOutputMix.outputMix = outputMixObject;

    // 输出
    dataSink.pFormat = nullptr;
    dataSink.pLocator = &locatorOutputMix;

    // 创建播放器
    result = (*engineEngine)->CreateAudioPlayer(engineEngine, &playerObject, &dataSource, &dataSink, INTERFACE_COUNT, interfaces, required);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "创建音频播放器失败");
        goto error;
    }

    // 获取配置
    result = (*playerObject)->GetInterface(playerObject, SL_IID_ANDROIDCONFIGURATION, &playerConfig);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "获取播放配置失败!");
        goto error;
    }

    // 设置性能模式
    result = (*playerConfig)->SetConfiguration(playerConfig, SL_ANDROID_KEY_PERFORMANCE_MODE, &performanceMode, sizeof(SLuint32));
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "设置播放配置失败");
        goto error;
    }

    // 初始化播放器
    result = (*playerObject)->Realize(playerObject, SL_BOOLEAN_FALSE);

    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "初始化播放器失败");
        goto error;
    }

    // 获取播放器接口
    result = (*playerObject)->GetInterface(playerObject, SL_IID_PLAY, &playerPlay);

    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "获取播放器失败");
        goto error;
    }

    // 获取回调接口对象
    result = (*playerObject)->GetInterface(playerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bufferQueue);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "获取回调接口对象失败");
        goto error;
    }

    playbackContext = new PlaybackContext{
        .minBufferSize = minBufferSize,
        .fd = dup(fd),
        .playerObject = playerObject,
        .engineObject = engineObject,
        .outputMixObject = outputMixObject,
        .buffer = new char[minBufferSize],
    };

    // 注册回调
    result = (*bufferQueue)->RegisterCallback(bufferQueue, bufferCallback, playbackContext);

    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "回调注册失败");
        goto error;
    }

    // 开始播放
    result = (*playerPlay)->SetPlayState(playerPlay, SL_PLAYSTATE_PLAYING);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "播放失败");
        goto error;
    }
    // 必须先喂下第一帧数据
    bufferCallback(bufferQueue, playbackContext);
    logger::info(TAG, "初始化完成");
    // 初始化完成
    return;
error:
    logger::info(TAG, "销毁对象");
    if (playbackContext != nullptr){
        delete playbackContext->buffer;
        delete playbackContext;
    }
    if (playerObject != nullptr){
        (*playerObject)->Destroy(playerObject);
        playerObject = nullptr;
    }
    if (outputMixObject != nullptr){
        (*outputMixObject)->Destroy(outputMixObject);
        outputMixObject = nullptr;
    }
    if (engineObject != nullptr){
        (*engineObject)->Destroy(engineObject);
        engineObject = nullptr;
    }
}

录音

头文件

struct RecorderCallbackContext {
    std::ofstream *pcmFile;
    std::vector<char> *buffer;
    SLRecordItf recorderRecord;
};

class OpenSLESRecorder {
private:
    SLObjectItf engineObject = nullptr;
    SLObjectItf recorderObject = nullptr;
    RecorderCallbackContext *context = nullptr;
    // 下面不需要自己回收
    SLRecordItf recorderRecord = nullptr;
public:
    explicit OpenSLESRecorder();
    ~OpenSLESRecorder();
};

源文件

void recordCallback(SLAndroidSimpleBufferQueueItf simpleBufferQueueItf, void* context){
    if (context == nullptr){
        return;
    }
    const auto recorderCallbackContext = reinterpret_cast<RecorderCallbackContext*>(context);
    SLuint32 state;
    auto result = (*recorderCallbackContext->recorderRecord)->GetRecordState(recorderCallbackContext->recorderRecord, &state);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "获取状态失败");
        throw std::runtime_error("Failed to get state");
    }
    switch (state) {
        case SL_RECORDSTATE_RECORDING: {
            logger::info(TAG, "录音中...");
            result = (*simpleBufferQueueItf)->Enqueue(simpleBufferQueueItf, recorderCallbackContext->buffer->data(), recorderCallbackContext->buffer->size());
            if (result != SL_RESULT_SUCCESS){
                logger::error(TAG, "引擎创建失败");
                throw std::runtime_error("Failed to create SL engine");
            }
            recorderCallbackContext->pcmFile->write(recorderCallbackContext->buffer->data(), static_cast<std::streamsize>(recorderCallbackContext->buffer->size()));
            break;
        }
        case SL_RECORDSTATE_PAUSED: {
            logger::info(TAG, "暂停状态...");
            break;
        }
        case SL_RECORDSTATE_STOPPED: {
            logger::info(TAG, "停止状态...");
            break;
        }
        default: {
            logger::info(TAG, "未知状态...");
        }
    }
}

OpenSLESRecorder::OpenSLESRecorder() {
    auto result = slCreateEngine(&engineObject, 0, nullptr, 0, nullptr, nullptr);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "引擎创建失败");
        throw std::runtime_error("Failed to create SL engine");
    }
    result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "引擎初始化失败");
        throw std::runtime_error("Failed to init SL engine");
    }
    SLEngineItf engineEngine;
    result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "获取engineEngine失败");
        throw std::runtime_error("Failed to get engineEngine");
    }
    // 输入数据
    SLDataLocator_IODevice ioDevice = {
            .locatorType = SL_DATALOCATOR_IODEVICE,
            .deviceType = SL_IODEVICE_AUDIOINPUT,
            .deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT,
            .device = nullptr
    };
    SLDataSource dataSource = {
            .pLocator = &ioDevice,
            .pFormat = nullptr
    };

    SLDataLocator_AndroidSimpleBufferQueue simpleBufferQueue = {
            .locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
            .numBuffers = 2,
    };

    SLDataFormat_PCM formatPcm = {
            .formatType= SL_DATAFORMAT_PCM,
            .numChannels = 2,
            .samplesPerSec = SL_SAMPLINGRATE_48,
            .bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16,
            .containerSize = SL_PCMSAMPLEFORMAT_FIXED_16,
            .channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
            .endianness = SL_BYTEORDER_LITTLEENDIAN
    };

    // 输出数据
    SLDataSink dataSink = {
            .pLocator = &simpleBufferQueue,
            .pFormat = &formatPcm,
    };
    const SLuint32 numInterfaces = 1;
    const SLInterfaceID  id[numInterfaces] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
    const SLboolean require[numInterfaces] = { SL_BOOLEAN_TRUE };
    result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &dataSource, &dataSink, numInterfaces, id, require);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "获取recorderObject失败");
        throw std::runtime_error("Failed to get recorderObject");
    }
    result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "recorder Realize 失败");
        throw std::runtime_error("Failed to Realize recorderObject");
    }

    result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "获取 recorderRecord 失败");
        throw std::runtime_error("Failed to get recorderRecord");
    }
    SLAndroidSimpleBufferQueueItf simpleBufferQueueItf;
    result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &simpleBufferQueueItf);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "获取 simpleBufferQueue 失败");
        throw std::runtime_error("Failed to get simpleBufferQueue");
    }

    auto *outPcmFile = new std::ofstream("/storage/emulated/0/Android/data/io.github.opensllearn/files/Music/pcm.pcm", std::ios::binary);

    if (!outPcmFile->is_open()) {
        logger::error(TAG, "文件打开错误");
        throw std::runtime_error("Failed to open file");
    }

    context = new RecorderCallbackContext{
            outPcmFile,
        new std::vector<char>(4096),
                recorderRecord,
    };

    result = (*simpleBufferQueueItf)->RegisterCallback(simpleBufferQueueItf, recordCallback, context);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "注册回调失败");
        throw std::runtime_error("Failed to RegisterCallback");
    }
    result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
    if (result != SL_RESULT_SUCCESS){
        logger::error(TAG, "开始录音频失败");
        throw std::runtime_error("Failed to record audio");
    }
    recordCallback(simpleBufferQueueItf, context);
}

OpenSLESRecorder::~OpenSLESRecorder() {
    logger::info(TAG, "停止+回收资源...");
    if (recorderRecord != nullptr){
        (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
    }
    if (context != nullptr){
        delete context->buffer;
        delete context->pcmFile;
        delete context;
    }
    if (recorderObject != nullptr) {
        (*recorderObject)->Destroy(recorderObject);
    }
    if (engineObject != nullptr){
        (*engineObject)->Destroy(engineObject);
    }
}
posted @ 2025-09-21 21:38  爱情丶眨眼而去  阅读(6)  评论(0)    收藏  举报