美文网首页
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