Android 万能音频播放器 三 ffmpeg + opensl 实现声音的播放

本文介绍如何在Android上使用FFmpeg进行音频解码和重采样,将得到的PCM数据通过OpenSL进行播放,详细探讨了采样率、采样位数和声道等概念,并提供了相关代码示例。
摘要由CSDN通过智能技术生成

Android 万能音频播放器 三 ffmpeg + OpenSL 实现声音的播放

前言

在上几篇的文章中已经讲到了如何用OpenSL播放PCM数据,本篇文章主要讲解如何使用FFmepg 解码和重采样出PCM数据,并使用OpenSL 进行播放,

主要知识点

  • 采样率:每秒对音频数据采样的个数(44100)
  • 采样位数:存储采样数据的位数(16bit 2字节)
  • 输出声道:单声道,立体声等
  • 重采样:就是把目标音频按照一定的采样率和采样位数重新采样编码成新的音频数据(为了统一格式)

  • -

代码


//音频解码所需信息封装
class WIAudio {
public:
    //音頻流索引浩
    int audio_stream_index;
    //音頻流信息
    AVCodecParameters *av_codec_par = NULL;
    //音频解码器上下文
    AVCodecContext *av_codec_ctx = NULL;
    //音频解码队列
    WIQueue *queueAudio = NULL;
    //播放状态
    WIPlayState *playState;

    //播放线程
    pthread_t thread_play;
    //压缩数据
    AVPacket *avPacket = NULL;

    int ret = -1;

    AVFrame *avFrame;
    //重采样上下文
    SwrContext *swrContext = NULL;
    //pcm缓冲区
    uint8_t *buffer;

    int data_size = 0;


//引擎对象
    SLObjectItf enginObject = NULL;
//引擎接口
    SLEngineItf engineItf = NULL;

//混音器对象(混音器作用是做声音处理)
    SLObjectItf outputmixObject = NULL;
//混音器环境接口
    SLEnvironmentalReverbItf outputEnvironmentalReverbItf = NULL;
    const SLEnvironmentalReverbSettings reverbSettings = SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR;

//播放器对象
    SLObjectItf pcmPlayerObject = NULL;
//播放接口
    SLPlayItf playItf = NULL;
//播放队列
    SLAndroidSimpleBufferQueueItf simpleBufferQueueItf = NULL;

    int sample_rate;
    void play();

    int resampleAudio();

    WIAudio(WIPlayState *playState,int sample_rate);

    ~WIAudio();

    void initopensl();

    int getresampleRate(int format);


};

#include "WIPlayer.h"

WIPlayer::WIPlayer(WIPlayState * playState,const char *url, WIPreparedListener *listener) {
    this->url = url;
    this->listener = listener;
    this->playState = playState;
}

WIPlayer::~WIPlayer() {

}

void *decodeFFmpeg(void *data) {
    //强制类型转换
    WIPlayer *player = (WIPlayer *) data;
    player->audioDecodeThread();
    pthread_exit((void *) player->decodeThread);
}

void WIPlayer::prepare() {
    //创建子线程 并将本身传入
    pthread_create(&decodeThread, NULL, decodeFFmpeg, this);
}

void WIPlayer::audioDecodeThread() {
    //注册所有组件并初始化网络
    av_register_all();
    avformat_network_init();

    av_format_ctx = avformat_alloc_context();

    //打开文件或网络流
    if (avformat_open_input(&av_format_ctx, url, NULL, NULL) != 0) {
        LOGE("can not open url :%s", url);
        return;
    }

    //获取流信息
    if (avformat_find_stream_info(av_format_ctx, NULL) < 0) {
        LOGE("can not find streams from %s", url);
        return;
    }

    //根据codecpar->codec_type获取音频流索引和音频流信息 此版本不能用streams[i]->codec来直接获取解码上下文
    for (int i = 0; i < av_format_ctx->nb_streams; ++i) {
        if (av_format_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
            if (audio == NULL) {
                audio = new WIAudio(playState,av_format_ctx->streams[i]->codecpar->sample_rate);
                audio->audio_stream_index = i;
                audio->av_codec_par = av_format_ctx->streams[i]->codecpar;
            }
        }
    }

    //获取解码器
    AVCodec *decoder = avcodec_find_decoder(audio->av_codec_par->codec_id);

    if (decoder == NULL) {
        LOGE("can not find decoder from %s", url);
        return;
    }

    //申请解码上下文
    audio->av_codec_ctx = avcodec_alloc_context3(decoder);

    if (!audio->av_codec_ctx) {
        LOGE("can not alloc accodecxtx");
        return;
    }

    //将音频流信息填充到解码上下文
    if (avcodec_parameters_to_context(audio->av_codec_ctx, audio->av_codec_par) < 0) {
        LOGE("can not parameters to context");
        return;
    }

    //打开解码器
    if (avcodec_open2(audio->av_codec_ctx, decoder, 0) != 0) {
        LOGE("can not open decoder from %s", url);
        return;
    }

    if (listener != NULL) {
        listener->onPrepared(CHILD_THREAD);
    }
}

void WIPlayer::start() {
    if (audio == NULL) {
        LOGE("audio is empty");
        return;
    }

    audio->play();

    int count = 0;
    //开始解码
    while (playState!=NULL&&!playState->exit) {
        //新api提供了avpacket的申请方式 。此语句不能再while外面 因为申请的已经释放,必须重新申请
        AVPacket *avPacket = av_packet_alloc();
        if (av_read_frame(av_format_ctx, avPacket) == 0) {
            if (avPacket->stream_index == audio->audio_stream_index) {
                count++;
                audio->queueAudio->putAvPacket(avPacket);
            } else {
                //释放avpacket
                av_packet_free(&avPacket);
                av_free(avPacket);
            }
        } else {
            LOGE("解码结束共%d帧", count);
            //释放avpacket
            av_packet_free(&avPacket);
            av_free(avPacket);
            avPacket = NULL;

            break;
        }
    }
}

项目地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值