美文网首页
android opensl 播放音频

android opensl 播放音频

作者: 小明叔叔_乐 | 来源:发表于2020-04-21 11:23 被阅读0次

一、思路:

在Jni层初始化好OpenSl,解码线程不停地从packet queue中获取packet(这个部分请参考上一篇文章),并解码(与视频解码类似,解码后的数据都是封装在AVFrame对象中),解码好的frame对象放到frame queue中;OpenSl启动后,会不停地从frame queue中获取frame对象,转化为PCM格式后,放到OpenSl的播放queue中即可正常播放。

二、上代码:

Jni层:
/**
 * 初始化opensl
 */
void AudioChannel::initOpenSl() {
    // 1.创建音频引擎 -- 2.设置混音器 -- 3.创建播放器(录音器) -- 4.设置缓冲队列和回调函数 -- 5.设置播放状态 -- 6.启动回调函数

    // 音频引擎
    SLEngineItf engineInteface = NULL;
    // 音频对象
    engineObject = NULL;
    // 混音器
    outputMixObject = NULL;
    // 播放器
    playerObject = NULL;
    // 回调接口
    playInteface = NULL;
    // 缓冲队列
    SLAndroidSimpleBufferQueueItf playerBufferQueue = NULL;

    // ========= 1.初始化播放引擎 =========
    SLresult result;
    result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
    if (result != SL_RESULT_SUCCESS) {
        return;
    }

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

    if (result != SL_RESULT_SUCCESS) {
        return;
    }

    // 获取到音频接口,相当于surfaceHolder
    result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineInteface);
    if (result != SL_RESULT_SUCCESS) {
        return;
    }

    // ========= 2.初始化混音引擎 =========
    result = (*engineInteface)->CreateOutputMix(engineInteface, &outputMixObject, 0, 0, 0);
    if (result != SL_RESULT_SUCCESS) {
        return;
    }

    // 初始化混音器outputMixObject
    result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
    if (result != SL_RESULT_SUCCESS) {
        return;
    }

    LOGD("channel = %d, sampleRate = %d, format = %d", decoderContext->channels,
         decoderContext->sample_rate, decoderContext->sample_fmt);
    SLuint32 channelMask = SL_SPEAKER_FRONT_CENTER;
    // 双声道
    if (decoderContext->channels == 2) {
        channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; // 立体声 (左前右后)
    }
    // configure audio source
    SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
                                                       2};      // 2个声道
    SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM,           // 播放的是PCM格式
                                   decoderContext->channels,                           // 2个声道
                                   (decoderContext->sample_rate *
                                    1000),        // sample_rate * 1000才是真正的采样率(可以把sample_rate打印出来,发现是441?)   因为音频的播放速度是由采样率决定的,所以音频的播放速率无需干预
                                   SL_PCMSAMPLEFORMAT_FIXED_16, // 位数 16位
                                   SL_PCMSAMPLEFORMAT_FIXED_16, // 和位数一致就可以
                                   channelMask,   //
                                   SL_BYTEORDER_LITTLEENDIAN};  // 小端模式

    SLDataSource audioSrc = {&loc_bufq, &format_pcm};

    // configure audio sink
    SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
    SLDataSink audioSnk = {&loc_outmix, NULL};

    const SLInterfaceID ids[1] = {SL_IID_BUFFERQUEUE};
    const SLboolean req[1] = {SL_BOOLEAN_TRUE};


    result = (*engineInteface)->CreateAudioPlayer(engineInteface,
                                                  &playerObject,  // 播放器
                                                  &audioSrc,      // 播放器参数 缓冲队列, 播放格式
                                                  &audioSnk,      // 播放缓冲区
                                                  1,              // 播放接口回调个数
                                                  ids,            // 设置播放器队列ID
                                                  req             // 是否采用内置的播放队列
    );

    if (result != SL_RESULT_SUCCESS) {
        return;
    }

    result = (*playerObject)->Realize(playerObject, SL_BOOLEAN_FALSE);
    if (result != SL_RESULT_SUCCESS) {
        return;
    }

    // get the play interface
    // 获取播放器接口
    result = (*playerObject)->GetInterface(playerObject, SL_IID_PLAY, &playInteface);
    if (result != SL_RESULT_SUCCESS) {
        return;
    }

    // get the buffer queue interface
    result = (*playerObject)->GetInterface(playerObject, SL_IID_BUFFERQUEUE,
                                           &playerBufferQueue);
    // get the buffer queue interface
    if (result != SL_RESULT_SUCCESS) {
        return;
    }

    // 注册回调
    // register callback on the buffer queue
    result = (*playerBufferQueue)->RegisterCallback(playerBufferQueue, bqPlayerCallback, this);
    if (result != SL_RESULT_SUCCESS) {
        return;
    }

    // set the player's state to playing
    // 设置播放状态 --- 播放/暂停/停止 状态
    result = (*playInteface)->SetPlayState(playInteface, SL_PLAYSTATE_PLAYING);
    if (result != SL_RESULT_SUCCESS) {
        return;
    }

    // 相当于手动调用播放按钮
    bqPlayerCallback(playerBufferQueue, this);
}

/**
 * this callback handler is called every time a buffer finishes playing
 * 会自动不断地回调
 * @param bq
 * @param context
 */
void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context) {
    AudioChannel *audioChannel = static_cast<AudioChannel *>(context);
    // pcm 原始音频数据
    // 数据存放在outBuffer里,返回值为outBuffer的有效长度, 音频的一个package里,有可能会有多帧数据?
    int dataLength = audioChannel->getPcm();

    if (audioChannel->isPlaying) {
        if (dataLength > 0) {
            // 压入队列里播放, 入参是buffer 和 数据的有效长度
            (*bq)->Enqueue(bq, audioChannel->out_buffer, dataLength);
        }
    } else {
        LOGD("audio channel callback stop.");
    }
}

/**
 * 获取PCM数据
 * @return PCM数据的有效长度
 */
int AudioChannel::getPcm() {
    AVFrame *frame = 0;
    int ret = 0;
    int out_buffer_size = 0;
//    int out_buffer_size2 = 0;
    while (isPlaying) {
        ret = frame_queue.get(frame);

        if (!isPlaying) {
            break;
        }

        if (!ret) {
            continue;
        }

        // 转换参数
        uint64_t dst_nb_samples = av_rescale_rnd(
                swr_get_delay(swrContext, frame->sample_rate) + frame->nb_samples,
                outputSampleRate,
                frame->sample_rate,
                AV_ROUND_UP
        );

//        LOGD("dst_nb_samples = %lld, frame->nb_samples = %d", dst_nb_samples,
//             frame->nb_samples);  // 2 * 44100
        // 转换 成 pcm,返回值为转换后的sample个数
        // out_buffer取址,传入,会在ffmpeg中进行初始化
        int nb = swr_convert(swrContext, &out_buffer, dst_nb_samples,
                             (const uint8_t **) (frame->data), frame->nb_samples);

        int out_channnel_nb = av_get_channel_layout_nb_channels(outputChannel);
        // buffer的大小是固定的,但是frame->data的大小不一定就等于buffer大小,所以要做一下处理
        // buffer的真实大小
        out_buffer_size = av_samples_get_buffer_size(NULL, out_channnel_nb, nb,
                                                     outputSamplaFormat, 1);

        // 设置当前音频播放的时间戳(音视频同步时使用) --- pts 显示时间戳; dts 解码时间戳 (B帧时,两都会不一样)
        colock = frame->pts * av_q2d(timeBase);

        break;
    }

    // 转换成功后,释放frame对象
    releaseFrame(frame);

    return out_buffer_size;
}

相关文章

网友评论

      本文标题:android opensl 播放音频

      本文链接:https://www.haomeiwen.com/subject/plnyihtx.html