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;
}
}
}