parent
2100aa82f5
commit
4786bf0200
@ -1,302 +0,0 @@ |
|||||||
//
|
|
||||||
// Created by frank on 2018/2/1.
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <jni.h> |
|
||||||
#include <cstring> |
|
||||||
#include <SLES/OpenSLES.h> |
|
||||||
#include <SLES/OpenSLES_Android.h> |
|
||||||
|
|
||||||
#ifdef __cplusplus |
|
||||||
extern "C" { |
|
||||||
#endif |
|
||||||
#include "libavcodec/avcodec.h" |
|
||||||
#include "libavformat/avformat.h" |
|
||||||
#include "libswscale/swscale.h" |
|
||||||
#include "libswresample/swresample.h" |
|
||||||
#include "libavutil/samplefmt.h" |
|
||||||
#include "libavutil/opt.h" |
|
||||||
#include "ffmpeg_jni_define.h" |
|
||||||
#ifdef __cplusplus |
|
||||||
} |
|
||||||
#endif |
|
||||||
|
|
||||||
#define TAG "OpenSLPlayer" |
|
||||||
|
|
||||||
//object of engine
|
|
||||||
SLObjectItf engineObject = nullptr; |
|
||||||
SLEngineItf engineEngine = nullptr; |
|
||||||
|
|
||||||
//object of mixer
|
|
||||||
SLObjectItf outputMixObject = nullptr; |
|
||||||
SLEnvironmentalReverbItf outputMixEnvironmentalReverb = nullptr; |
|
||||||
|
|
||||||
//object of buffer
|
|
||||||
SLPlayItf mPlayerPlay = nullptr; |
|
||||||
SLVolumeItf mPlayerVolume = nullptr; |
|
||||||
SLObjectItf mPlayerObject = nullptr; |
|
||||||
SLEffectSendItf mPlayerEffectSend = nullptr; |
|
||||||
SLAndroidSimpleBufferQueueItf mPlayerBufferQueue = nullptr; |
|
||||||
|
|
||||||
|
|
||||||
//audio effect
|
|
||||||
void *openBuffer; |
|
||||||
size_t bufferSize; |
|
||||||
uint8_t *outputBuffer; |
|
||||||
size_t outputBufferSize; |
|
||||||
const SLEnvironmentalReverbSettings reverbSettings = SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR; |
|
||||||
|
|
||||||
AVPacket packet; |
|
||||||
int audioStream; |
|
||||||
AVFrame *aFrame; |
|
||||||
SwrContext *swr; |
|
||||||
int frame_count = 0; |
|
||||||
AVFormatContext *aFormatCtx; |
|
||||||
AVCodecContext *aCodecCtx; |
|
||||||
|
|
||||||
int releaseAudioPlayer(); |
|
||||||
|
|
||||||
int getPCMData(void **pcm, size_t *pcmSize); |
|
||||||
|
|
||||||
int createAudioPlayer(int *rate, int *channel, const char *file_name); |
|
||||||
|
|
||||||
|
|
||||||
void audioCallback(SLAndroidSimpleBufferQueueItf bufferQueueItf, void *context) { |
|
||||||
bufferSize = 0; |
|
||||||
getPCMData(&openBuffer, &bufferSize); |
|
||||||
if (nullptr != openBuffer && 0 != bufferSize) { |
|
||||||
SLresult result; |
|
||||||
result = (*mPlayerBufferQueue)->Enqueue(mPlayerBufferQueue, openBuffer, bufferSize); |
|
||||||
if (result < 0) { |
|
||||||
LOGE(TAG, "Enqueue error..."); |
|
||||||
} else { |
|
||||||
LOGI(TAG, "decode frame count=%d", frame_count++); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
//create the engine of OpenSLES
|
|
||||||
SLresult createEngine() { |
|
||||||
SLresult result; |
|
||||||
result = slCreateEngine(&engineObject, 0, nullptr, |
|
||||||
0, nullptr, nullptr); |
|
||||||
if (result != SL_RESULT_SUCCESS) { |
|
||||||
LOGE(TAG, "slCreateEngine error=%d", result); |
|
||||||
return result; |
|
||||||
} |
|
||||||
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); |
|
||||||
if (result != SL_RESULT_SUCCESS) { |
|
||||||
LOGE(TAG, "engineObject->Realize error=%d", result); |
|
||||||
return result; |
|
||||||
} |
|
||||||
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine); |
|
||||||
if (result != SL_RESULT_SUCCESS) { |
|
||||||
LOGE(TAG, "engineObject->GetInterface error=%d", result); |
|
||||||
return result; |
|
||||||
} |
|
||||||
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, nullptr, nullptr); |
|
||||||
if (result != SL_RESULT_SUCCESS) { |
|
||||||
LOGE(TAG, "engineEngine->CreateOutputMix error=%d", result); |
|
||||||
return result; |
|
||||||
} |
|
||||||
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE); |
|
||||||
if (result != SL_RESULT_SUCCESS) { |
|
||||||
LOGE(TAG, "outputMixObject->Realize error=%d", result); |
|
||||||
return result; |
|
||||||
} |
|
||||||
result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB, |
|
||||||
&outputMixEnvironmentalReverb); |
|
||||||
if (result != SL_RESULT_SUCCESS) { |
|
||||||
LOGE(TAG, "outputMixObject->GetInterface error=%d", result); |
|
||||||
return result; |
|
||||||
} |
|
||||||
result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties( |
|
||||||
outputMixEnvironmentalReverb, &reverbSettings); |
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
SLresult createBufferQueueAudioPlayer(int rate, int channel, int bitsPerSample) { |
|
||||||
SLresult result; |
|
||||||
|
|
||||||
//config audio source
|
|
||||||
SLDataLocator_AndroidSimpleBufferQueue buffer_queue = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, |
|
||||||
2}; |
|
||||||
SLDataFormat_PCM format_pcm; |
|
||||||
format_pcm.formatType = SL_DATAFORMAT_PCM; |
|
||||||
format_pcm.numChannels = (SLuint32) channel; |
|
||||||
format_pcm.bitsPerSample = (SLuint32) bitsPerSample; |
|
||||||
format_pcm.samplesPerSec = (SLuint32) (rate * 1000); |
|
||||||
format_pcm.containerSize = 16; |
|
||||||
if (channel == 2) |
|
||||||
format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; |
|
||||||
else |
|
||||||
format_pcm.channelMask = SL_SPEAKER_FRONT_CENTER; |
|
||||||
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; |
|
||||||
SLDataSource audioSrc = {&buffer_queue, &format_pcm}; |
|
||||||
|
|
||||||
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject}; |
|
||||||
SLDataSink audioSnk = {&loc_outmix, nullptr}; |
|
||||||
|
|
||||||
const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND, SL_IID_VOLUME}; |
|
||||||
const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; |
|
||||||
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &mPlayerObject, &audioSrc, &audioSnk, |
|
||||||
3, ids, req); |
|
||||||
if (result != SL_RESULT_SUCCESS) { |
|
||||||
LOGE(TAG, "outputMixObject->GetInterface error=%d", result); |
|
||||||
return result; |
|
||||||
} |
|
||||||
result = (*mPlayerObject)->Realize(mPlayerObject, SL_BOOLEAN_FALSE); |
|
||||||
if (result != SL_RESULT_SUCCESS) { |
|
||||||
LOGE(TAG, "mPlayerObject->Realize error=%d", result); |
|
||||||
return result; |
|
||||||
} |
|
||||||
(*mPlayerObject)->GetInterface(mPlayerObject, SL_IID_PLAY, &mPlayerPlay); |
|
||||||
(*mPlayerObject)->GetInterface(mPlayerObject, SL_IID_BUFFERQUEUE, &mPlayerBufferQueue); |
|
||||||
result = (*mPlayerBufferQueue)->RegisterCallback(mPlayerBufferQueue, audioCallback, nullptr); |
|
||||||
if (result != SL_RESULT_SUCCESS) { |
|
||||||
LOGE(TAG, "mPlayerBufferQueue->RegisterCallback error=%d", result); |
|
||||||
return result; |
|
||||||
} |
|
||||||
(*mPlayerObject)->GetInterface(mPlayerObject, SL_IID_EFFECTSEND, &mPlayerEffectSend); |
|
||||||
(*mPlayerObject)->GetInterface(mPlayerObject, SL_IID_VOLUME, &mPlayerVolume); |
|
||||||
result = (*mPlayerPlay)->SetPlayState(mPlayerPlay, SL_PLAYSTATE_PLAYING); |
|
||||||
return result; |
|
||||||
} |
|
||||||
|
|
||||||
int createAudioPlayer(int *rate, int *channel, const char *file_name) { |
|
||||||
|
|
||||||
av_register_all(); |
|
||||||
aFormatCtx = avformat_alloc_context(); |
|
||||||
|
|
||||||
if (avformat_open_input(&aFormatCtx, file_name, nullptr, nullptr) != 0) { |
|
||||||
LOGE(TAG, "Couldn't open file:%s\n", file_name); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
if (avformat_find_stream_info(aFormatCtx, nullptr) < 0) { |
|
||||||
LOGE(TAG, "Couldn't find stream information."); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
int i; |
|
||||||
audioStream = -1; |
|
||||||
for (i = 0; i < aFormatCtx->nb_streams; i++) { |
|
||||||
if (aFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && |
|
||||||
audioStream < 0) { |
|
||||||
audioStream = i; |
|
||||||
} |
|
||||||
} |
|
||||||
if (audioStream == -1) { |
|
||||||
LOGE(TAG, "Couldn't find audio stream!"); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
aCodecCtx = aFormatCtx->streams[audioStream]->codec; |
|
||||||
AVCodec *aCodec = avcodec_find_decoder(aCodecCtx->codec_id); |
|
||||||
if (!aCodec) { |
|
||||||
fprintf(stderr, "Unsupported codec!\n"); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
if (avcodec_open2(aCodecCtx, aCodec, nullptr) < 0) { |
|
||||||
LOGE(TAG, "Could not open codec."); |
|
||||||
return -1; |
|
||||||
} |
|
||||||
aFrame = av_frame_alloc(); |
|
||||||
swr = swr_alloc(); |
|
||||||
av_opt_set_int(swr, "in_channel_layout", aCodecCtx->channel_layout, 0); |
|
||||||
av_opt_set_int(swr, "out_channel_layout", aCodecCtx->channel_layout, 0); |
|
||||||
av_opt_set_int(swr, "in_sample_rate", aCodecCtx->sample_rate, 0); |
|
||||||
av_opt_set_int(swr, "out_sample_rate", aCodecCtx->sample_rate, 0); |
|
||||||
av_opt_set_sample_fmt(swr, "in_sample_fmt", aCodecCtx->sample_fmt, 0); |
|
||||||
av_opt_set_sample_fmt(swr, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0); |
|
||||||
swr_init(swr); |
|
||||||
|
|
||||||
outputBufferSize = 8196; |
|
||||||
outputBuffer = (uint8_t *) malloc(sizeof(uint8_t) * outputBufferSize); |
|
||||||
*rate = aCodecCtx->sample_rate; |
|
||||||
*channel = aCodecCtx->channels; |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
int getPCMData(void **pcm, size_t *pcmSize) { |
|
||||||
while (av_read_frame(aFormatCtx, &packet) >= 0) { |
|
||||||
int frameFinished = 0; |
|
||||||
//is audio stream
|
|
||||||
if (packet.stream_index == audioStream) { |
|
||||||
avcodec_decode_audio4(aCodecCtx, aFrame, &frameFinished, &packet); |
|
||||||
//decode success
|
|
||||||
if (frameFinished) { |
|
||||||
int data_size = av_samples_get_buffer_size( |
|
||||||
aFrame->linesize, aCodecCtx->channels, |
|
||||||
aFrame->nb_samples, aCodecCtx->sample_fmt, 1); |
|
||||||
|
|
||||||
if (data_size > outputBufferSize) { |
|
||||||
outputBufferSize = (size_t) data_size; |
|
||||||
outputBuffer = (uint8_t *) realloc(outputBuffer,sizeof(uint8_t) * outputBufferSize); |
|
||||||
} |
|
||||||
|
|
||||||
swr_convert(swr, &outputBuffer, aFrame->nb_samples, |
|
||||||
(uint8_t const **) (aFrame->extended_data), |
|
||||||
aFrame->nb_samples); |
|
||||||
|
|
||||||
*pcm = outputBuffer; |
|
||||||
*pcmSize = (size_t) data_size; |
|
||||||
return 0; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
return -1; |
|
||||||
} |
|
||||||
|
|
||||||
int releaseAudioPlayer() { |
|
||||||
av_packet_unref(&packet); |
|
||||||
av_free(outputBuffer); |
|
||||||
av_free(aFrame); |
|
||||||
avcodec_free_context(&aCodecCtx); |
|
||||||
avformat_close_input(&aFormatCtx); |
|
||||||
return 0; |
|
||||||
} |
|
||||||
|
|
||||||
AUDIO_PLAYER_FUNC(void, playAudio, jstring filePath) { |
|
||||||
|
|
||||||
int rate, channel; |
|
||||||
const char *file_name = env->GetStringUTFChars(filePath, nullptr); |
|
||||||
LOGI(TAG, "file_name=%s", file_name); |
|
||||||
|
|
||||||
int ret = createAudioPlayer(&rate, &channel, file_name); |
|
||||||
if (ret < 0) |
|
||||||
return; |
|
||||||
SLresult result = createEngine(); |
|
||||||
if (result != SL_RESULT_SUCCESS) |
|
||||||
return; |
|
||||||
result = createBufferQueueAudioPlayer(rate, channel, SL_PCMSAMPLEFORMAT_FIXED_16); |
|
||||||
if (result != SL_RESULT_SUCCESS) |
|
||||||
return; |
|
||||||
audioCallback(mPlayerBufferQueue, nullptr); |
|
||||||
} |
|
||||||
|
|
||||||
AUDIO_PLAYER_FUNC(void, stop) { |
|
||||||
if (mPlayerObject != nullptr) { |
|
||||||
(*mPlayerObject)->Destroy(mPlayerObject); |
|
||||||
mPlayerObject = nullptr; |
|
||||||
mPlayerPlay = nullptr; |
|
||||||
mPlayerBufferQueue = nullptr; |
|
||||||
mPlayerEffectSend = nullptr; |
|
||||||
mPlayerVolume = nullptr; |
|
||||||
} |
|
||||||
|
|
||||||
if (outputMixObject != nullptr) { |
|
||||||
(*outputMixObject)->Destroy(outputMixObject); |
|
||||||
outputMixObject = nullptr; |
|
||||||
outputMixEnvironmentalReverb = nullptr; |
|
||||||
} |
|
||||||
|
|
||||||
if (engineObject != nullptr) { |
|
||||||
(*engineObject)->Destroy(engineObject); |
|
||||||
engineObject = nullptr; |
|
||||||
engineEngine = nullptr; |
|
||||||
} |
|
||||||
|
|
||||||
releaseAudioPlayer(); |
|
||||||
} |
|
Loading…
Reference in new issue