parent
709b994c7d
commit
17b18af513
@ -0,0 +1,154 @@ |
|||||||
|
//
|
||||||
|
// Created by frank on 2018/2/1.
|
||||||
|
//
|
||||||
|
#include <jni.h> |
||||||
|
#include <stdlib.h> |
||||||
|
#include <unistd.h> |
||||||
|
//封装格式
|
||||||
|
#include "libavformat/avformat.h" |
||||||
|
//解码
|
||||||
|
#include "libavcodec/avcodec.h" |
||||||
|
//缩放
|
||||||
|
#include "libswscale/swscale.h" |
||||||
|
//重采样
|
||||||
|
#include "libswresample/swresample.h" |
||||||
|
#include <android/log.h> |
||||||
|
|
||||||
|
#define TAG "MediaPlayer" |
||||||
|
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO, TAG, FORMAT, ##__VA_ARGS__); |
||||||
|
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR, TAG, FORMAT, ##__VA_ARGS__); |
||||||
|
|
||||||
|
#define MAX_AUDIO_FRAME_SIZE 48000 * 4 |
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_com_frank_ffmpeg_AudioPlayer_play |
||||||
|
(JNIEnv *env, jobject jthiz, jstring input_jstr){ |
||||||
|
const char* input_cstr = (*env)->GetStringUTFChars(env,input_jstr,NULL); |
||||||
|
LOGI("input_cstr=%s", input_cstr); |
||||||
|
//注册组件
|
||||||
|
av_register_all(); |
||||||
|
AVFormatContext *pFormatCtx = avformat_alloc_context(); |
||||||
|
//打开音频文件
|
||||||
|
if(avformat_open_input(&pFormatCtx,input_cstr,NULL,NULL) != 0){ |
||||||
|
LOGI("%s","无法打开音频文件"); |
||||||
|
return; |
||||||
|
} |
||||||
|
//获取输入文件信息
|
||||||
|
if(avformat_find_stream_info(pFormatCtx,NULL) < 0){ |
||||||
|
LOGI("%s","无法获取输入文件信息"); |
||||||
|
return; |
||||||
|
} |
||||||
|
//获取音频流索引位置
|
||||||
|
int i = 0, audio_stream_idx = -1; |
||||||
|
for(; i < pFormatCtx->nb_streams;i++){ |
||||||
|
if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO){ |
||||||
|
audio_stream_idx = i; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//获取音频解码器
|
||||||
|
AVCodecContext *codecCtx = pFormatCtx->streams[audio_stream_idx]->codec; |
||||||
|
AVCodec *codec = avcodec_find_decoder(codecCtx->codec_id); |
||||||
|
if(codec == NULL){ |
||||||
|
LOGI("%s","无法获取解码器"); |
||||||
|
return; |
||||||
|
} |
||||||
|
//打开解码器
|
||||||
|
if(avcodec_open2(codecCtx,codec,NULL) < 0){ |
||||||
|
LOGI("%s","无法打开解码器"); |
||||||
|
return; |
||||||
|
} |
||||||
|
//压缩数据
|
||||||
|
AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket)); |
||||||
|
//解压缩数据
|
||||||
|
AVFrame *frame = av_frame_alloc(); |
||||||
|
//frame->16bit 44100 PCM 统一音频采样格式与采样率
|
||||||
|
SwrContext *swrCtx = swr_alloc(); |
||||||
|
|
||||||
|
//输入的采样格式
|
||||||
|
enum AVSampleFormat in_sample_fmt = codecCtx->sample_fmt; |
||||||
|
//输出采样格式16bit PCM
|
||||||
|
enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16; |
||||||
|
//输入采样率
|
||||||
|
int in_sample_rate = codecCtx->sample_rate; |
||||||
|
//输出采样率
|
||||||
|
int out_sample_rate = in_sample_rate; |
||||||
|
//声道布局(2个声道,默认立体声stereo)
|
||||||
|
uint64_t in_ch_layout = codecCtx->channel_layout; |
||||||
|
//输出的声道布局(立体声)
|
||||||
|
uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO; |
||||||
|
|
||||||
|
swr_alloc_set_opts(swrCtx, |
||||||
|
out_ch_layout,out_sample_fmt,out_sample_rate, |
||||||
|
in_ch_layout,in_sample_fmt,in_sample_rate, |
||||||
|
0, NULL); |
||||||
|
swr_init(swrCtx); |
||||||
|
|
||||||
|
//输出的声道个数
|
||||||
|
int out_channel_nb = av_get_channel_layout_nb_channels(out_ch_layout); |
||||||
|
|
||||||
|
jclass player_class = (*env)->GetObjectClass(env,jthiz); |
||||||
|
if(!player_class){ |
||||||
|
LOGE("player_class not found..."); |
||||||
|
} |
||||||
|
//AudioTrack对象
|
||||||
|
jmethodID audio_track_method = (*env)->GetMethodID(env,player_class,"createAudioTrack","(II)Landroid/media/AudioTrack;"); |
||||||
|
if(!audio_track_method){ |
||||||
|
LOGE("audio_track_method not found..."); |
||||||
|
} |
||||||
|
jobject audio_track = (*env)->CallObjectMethod(env,jthiz,audio_track_method,out_sample_rate,out_channel_nb); |
||||||
|
|
||||||
|
//调用play方法
|
||||||
|
jclass audio_track_class = (*env)->GetObjectClass(env,audio_track); |
||||||
|
jmethodID audio_track_play_mid = (*env)->GetMethodID(env,audio_track_class,"play","()V"); |
||||||
|
(*env)->CallVoidMethod(env,audio_track,audio_track_play_mid); |
||||||
|
|
||||||
|
//获取write()方法
|
||||||
|
jmethodID audio_track_write_mid = (*env)->GetMethodID(env,audio_track_class,"write","([BII)I"); |
||||||
|
|
||||||
|
//16bit 44100 PCM 数据
|
||||||
|
uint8_t *out_buffer = (uint8_t *)av_malloc(MAX_AUDIO_FRAME_SIZE); |
||||||
|
|
||||||
|
int got_frame = 0,index = 0, ret; |
||||||
|
//不断读取编码数据
|
||||||
|
while(av_read_frame(pFormatCtx,packet) >= 0){ |
||||||
|
//解码音频类型的Packet
|
||||||
|
if(packet->stream_index == audio_stream_idx){ |
||||||
|
//解码
|
||||||
|
ret = avcodec_decode_audio4(codecCtx,frame,&got_frame,packet); |
||||||
|
if(ret < 0){ |
||||||
|
break; |
||||||
|
} |
||||||
|
//解码一帧成功
|
||||||
|
if(got_frame > 0){ |
||||||
|
LOGI("decode frame count=%d",index++); |
||||||
|
//音频格式转换
|
||||||
|
swr_convert(swrCtx, &out_buffer, MAX_AUDIO_FRAME_SIZE,(const uint8_t **)frame->data,frame->nb_samples); |
||||||
|
int out_buffer_size = av_samples_get_buffer_size(NULL, out_channel_nb, |
||||||
|
frame->nb_samples, out_sample_fmt, 1); |
||||||
|
|
||||||
|
jbyteArray audio_sample_array = (*env)->NewByteArray(env,out_buffer_size); |
||||||
|
jbyte* sample_byte_array = (*env)->GetByteArrayElements(env,audio_sample_array,NULL); |
||||||
|
//拷贝缓冲数据
|
||||||
|
memcpy(sample_byte_array, out_buffer, (size_t) out_buffer_size); |
||||||
|
//释放数组
|
||||||
|
(*env)->ReleaseByteArrayElements(env,audio_sample_array,sample_byte_array,0); |
||||||
|
//调用AudioTrack的write方法进行播放
|
||||||
|
(*env)->CallIntMethod(env,audio_track,audio_track_write_mid, |
||||||
|
audio_sample_array,0,out_buffer_size); |
||||||
|
//释放局部引用
|
||||||
|
(*env)->DeleteLocalRef(env,audio_sample_array); |
||||||
|
usleep(1000 * 16); |
||||||
|
} |
||||||
|
} |
||||||
|
av_free_packet(packet); |
||||||
|
} |
||||||
|
LOGI("decode audio finish"); |
||||||
|
av_frame_free(&frame); |
||||||
|
av_free(out_buffer); |
||||||
|
swr_free(&swrCtx); |
||||||
|
avcodec_close(codecCtx); |
||||||
|
avformat_close_input(&pFormatCtx); |
||||||
|
(*env)->ReleaseStringUTFChars(env,input_jstr,input_cstr); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,319 @@ |
|||||||
|
//
|
||||||
|
// Created by frank on 2018/2/1.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <jni.h> |
||||||
|
#include <string.h> |
||||||
|
#include "libavcodec/avcodec.h" |
||||||
|
#include "libavformat/avformat.h" |
||||||
|
#include "libswscale/swscale.h" |
||||||
|
#include "libswresample/swresample.h" |
||||||
|
#include "libavutil/samplefmt.h" |
||||||
|
#include <SLES/OpenSLES.h> |
||||||
|
#include <SLES/OpenSLES_Android.h> |
||||||
|
#include <android/log.h> |
||||||
|
#include <libavutil/opt.h> |
||||||
|
|
||||||
|
#define TAG "OpenSLPlayer" |
||||||
|
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO, TAG, FORMAT,##__VA_ARGS__); |
||||||
|
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR, TAG, FORMAT,##__VA_ARGS__); |
||||||
|
|
||||||
|
//引擎接口
|
||||||
|
SLObjectItf engineObject = NULL; |
||||||
|
SLEngineItf engineEngine; |
||||||
|
|
||||||
|
//输出混音器接口
|
||||||
|
SLObjectItf outputMixObject = NULL; |
||||||
|
SLEnvironmentalReverbItf outputMixEnvironmentalReverb = NULL; |
||||||
|
|
||||||
|
//缓冲播放器接口
|
||||||
|
SLObjectItf bqPlayerObject = NULL; |
||||||
|
SLPlayItf bqPlayerPlay; |
||||||
|
SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue; |
||||||
|
SLEffectSendItf bqPlayerEffectSend; |
||||||
|
SLVolumeItf bqPlayerVolume; |
||||||
|
|
||||||
|
//音效设置
|
||||||
|
const SLEnvironmentalReverbSettings reverbSettings = SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR; |
||||||
|
void *buffer; |
||||||
|
size_t bufferSize; |
||||||
|
uint8_t *outputBuffer; |
||||||
|
size_t outputBufferSize; |
||||||
|
|
||||||
|
//FFmpeg相关
|
||||||
|
AVPacket packet; |
||||||
|
int audioStream; |
||||||
|
AVFrame *aFrame; |
||||||
|
SwrContext *swr; |
||||||
|
AVFormatContext *aFormatCtx; |
||||||
|
AVCodecContext *aCodecCtx; |
||||||
|
int frame_count = 0; |
||||||
|
|
||||||
|
int createAudioPlayer(int *rate, int *channel, const char *file_name) ; |
||||||
|
|
||||||
|
// 释放相关资源
|
||||||
|
int releaseAudioPlayer(); |
||||||
|
|
||||||
|
// 获取PCM数据, 自动回调获取
|
||||||
|
int getPCM(void **pcm, size_t *pcmSize) ; |
||||||
|
|
||||||
|
//播放回调方法
|
||||||
|
void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bufferQueueItf, void *context) { |
||||||
|
bufferSize = 0; |
||||||
|
getPCM(&buffer, &bufferSize); |
||||||
|
//如果buffer不为空,入待播放队列
|
||||||
|
if (NULL != buffer && 0 != bufferSize) { |
||||||
|
SLresult result; |
||||||
|
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer, bufferSize); |
||||||
|
if(result < 0){ |
||||||
|
LOGE("Enqueue error..."); |
||||||
|
} else{ |
||||||
|
LOGI("decode frame count=%d", frame_count++); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//创建OpenSLES引擎
|
||||||
|
void createEngine() { |
||||||
|
SLresult result; |
||||||
|
//创建引擎
|
||||||
|
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL); |
||||||
|
LOGI("slCreateEngine=%d", result); |
||||||
|
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE); |
||||||
|
LOGI("engineObject->Realize=%d", result); |
||||||
|
//获取引擎接口
|
||||||
|
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine); |
||||||
|
LOGI("engineObject->GetInterface=%d", result); |
||||||
|
//创建输出混音器
|
||||||
|
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, 0, 0); |
||||||
|
LOGI("CreateOutputMix=%d", result); |
||||||
|
//关联输出混音器
|
||||||
|
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE); |
||||||
|
LOGI("outputMixObject->Realize=%d", result); |
||||||
|
//获取reverb接口
|
||||||
|
result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB, |
||||||
|
&outputMixEnvironmentalReverb); |
||||||
|
LOGI("outputMixObject->GetInterface=%d", result); |
||||||
|
if (SL_RESULT_SUCCESS == result) { |
||||||
|
result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties( |
||||||
|
outputMixEnvironmentalReverb, &reverbSettings); |
||||||
|
} |
||||||
|
LOGI("SetEnvironmentalReverbProperties=%d", result); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
//创建带有缓冲队列的音频播放器
|
||||||
|
void createBufferQueueAudioPlayer(int rate, int channel, int bitsPerSample) { |
||||||
|
SLresult result; |
||||||
|
|
||||||
|
//配置音频源
|
||||||
|
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, NULL}; |
||||||
|
|
||||||
|
//创建音频播放器
|
||||||
|
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, &bqPlayerObject, &audioSrc, &audioSnk, |
||||||
|
3, ids, req); |
||||||
|
LOGI("CreateAudioPlayer=%d", result); |
||||||
|
|
||||||
|
//关联播放器
|
||||||
|
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE); |
||||||
|
LOGI("bqPlayerObject Realize=%d", result); |
||||||
|
|
||||||
|
//获取播放接口
|
||||||
|
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay); |
||||||
|
LOGI("GetInterface bqPlayerPlay=%d", result); |
||||||
|
|
||||||
|
//获取缓冲队列接口
|
||||||
|
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE, |
||||||
|
&bqPlayerBufferQueue); |
||||||
|
LOGI("GetInterface bqPlayerBufferQueue=%d", result); |
||||||
|
|
||||||
|
//注册缓冲队列回调
|
||||||
|
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL); |
||||||
|
LOGI("RegisterCallback=%d", result); |
||||||
|
|
||||||
|
//获取音效接口
|
||||||
|
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_EFFECTSEND, |
||||||
|
&bqPlayerEffectSend); |
||||||
|
LOGI("GetInterface effect=%d", result); |
||||||
|
|
||||||
|
//获取音量接口
|
||||||
|
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume); |
||||||
|
LOGI("GetInterface volume=%d", result); |
||||||
|
|
||||||
|
//开始播放音乐
|
||||||
|
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING); |
||||||
|
LOGI("SetPlayState=%d", 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, NULL, NULL) != 0) { |
||||||
|
LOGE("Couldn't open file:%s\n", file_name); |
||||||
|
return -1; // Couldn't open file
|
||||||
|
} |
||||||
|
|
||||||
|
//寻找stream信息
|
||||||
|
if (avformat_find_stream_info(aFormatCtx, NULL) < 0) { |
||||||
|
LOGE("Couldn't find stream information."); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
//寻找音频stream
|
||||||
|
int i; |
||||||
|
audioStream = -1; |
||||||
|
for (i = 0; i < aFormatCtx->nb_streams; i++) { |
||||||
|
if (aFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && |
||||||
|
audioStream < 0) { |
||||||
|
audioStream = i; |
||||||
|
} |
||||||
|
} |
||||||
|
if (audioStream == -1) { |
||||||
|
LOGE("Couldn't find audio stream!"); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
//获取解码器context
|
||||||
|
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, NULL) < 0) { |
||||||
|
LOGE("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); |
||||||
|
|
||||||
|
// 分配PCM数据缓存
|
||||||
|
outputBufferSize = 8196; |
||||||
|
outputBuffer = (uint8_t *) malloc(sizeof(uint8_t) * outputBufferSize); |
||||||
|
// 返回sample rate和channels
|
||||||
|
*rate = aCodecCtx->sample_rate; |
||||||
|
*channel = aCodecCtx->channels; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
// 获取PCM数据, 自动回调获取
|
||||||
|
int getPCM(void **pcm, size_t *pcmSize) { |
||||||
|
while (av_read_frame(aFormatCtx, &packet) >= 0) { |
||||||
|
int frameFinished = 0; |
||||||
|
//音频流
|
||||||
|
if (packet.stream_index == audioStream) { |
||||||
|
avcodec_decode_audio4(aCodecCtx, aFrame, &frameFinished, &packet); |
||||||
|
//解码完一帧数据
|
||||||
|
if (frameFinished) { |
||||||
|
// data_size为音频数据所占的字节数
|
||||||
|
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数据
|
||||||
|
*pcm = outputBuffer; |
||||||
|
*pcmSize = (size_t) data_size; |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
// 释放相关资源
|
||||||
|
int releaseAudioPlayer() { |
||||||
|
av_packet_unref(&packet); |
||||||
|
av_free(outputBuffer); |
||||||
|
av_free(aFrame); |
||||||
|
avcodec_close(aCodecCtx); |
||||||
|
avformat_close_input(&aFormatCtx); |
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
JNIEXPORT void JNICALL Java_com_frank_ffmpeg_AudioPlayer_playAudio |
||||||
|
(JNIEnv * env, jclass jobject, jstring filePath) { |
||||||
|
|
||||||
|
int rate, channel; |
||||||
|
const char *file_name = (*env)->GetStringUTFChars(env, filePath, NULL); |
||||||
|
LOGI("file_name=%s", file_name); |
||||||
|
|
||||||
|
// 创建音频解码器
|
||||||
|
createAudioPlayer(&rate, &channel, file_name); |
||||||
|
|
||||||
|
// 创建播放引擎
|
||||||
|
createEngine(); |
||||||
|
|
||||||
|
// 创建缓冲队列音频播放器
|
||||||
|
createBufferQueueAudioPlayer(rate, channel, SL_PCMSAMPLEFORMAT_FIXED_16); |
||||||
|
|
||||||
|
// 启动音频播放
|
||||||
|
bqPlayerCallback(bqPlayerBufferQueue, NULL); |
||||||
|
} |
||||||
|
|
||||||
|
//停止播放,释放相关资源
|
||||||
|
JNIEXPORT jint JNICALL Java_com_frank_ffmpeg_AudioPlayer_stop |
||||||
|
(JNIEnv * env, jclass jobject) { |
||||||
|
if (bqPlayerObject != NULL) { |
||||||
|
(*bqPlayerObject)->Destroy(bqPlayerObject); |
||||||
|
bqPlayerObject = NULL; |
||||||
|
bqPlayerPlay = NULL; |
||||||
|
bqPlayerBufferQueue = NULL; |
||||||
|
bqPlayerEffectSend = NULL; |
||||||
|
bqPlayerVolume = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
if (outputMixObject != NULL) { |
||||||
|
(*outputMixObject)->Destroy(outputMixObject); |
||||||
|
outputMixObject = NULL; |
||||||
|
outputMixEnvironmentalReverb = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
if (engineObject != NULL) { |
||||||
|
(*engineObject)->Destroy(engineObject); |
||||||
|
engineObject = NULL; |
||||||
|
engineEngine = NULL; |
||||||
|
} |
||||||
|
|
||||||
|
// 释放解码器相关资源
|
||||||
|
releaseAudioPlayer(); |
||||||
|
} |
@ -0,0 +1,45 @@ |
|||||||
|
package com.frank.ffmpeg; |
||||||
|
|
||||||
|
import android.media.AudioFormat; |
||||||
|
import android.media.AudioManager; |
||||||
|
import android.media.AudioTrack; |
||||||
|
|
||||||
|
/** |
||||||
|
* 音频播放器 |
||||||
|
* Created by frank on 2018/2/1. |
||||||
|
*/ |
||||||
|
|
||||||
|
public class AudioPlayer { |
||||||
|
static { |
||||||
|
System.loadLibrary("media-handle"); |
||||||
|
} |
||||||
|
//调用AudioTrack播放
|
||||||
|
public native void play(String audioPath); |
||||||
|
//调用OpenSL ES播放
|
||||||
|
public native void playAudio(String audioPath); |
||||||
|
//调用OpenSL ES播放
|
||||||
|
public native void stop(String audioPath); |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建一个AudioTrack对象 |
||||||
|
* @param sampleRate 采样率 |
||||||
|
* @param channels 声道布局 |
||||||
|
* @return AudioTrack |
||||||
|
*/ |
||||||
|
public AudioTrack createAudioTrack(int sampleRate, int channels){ |
||||||
|
int audioFormat = AudioFormat.ENCODING_PCM_16BIT; |
||||||
|
int channelConfig; |
||||||
|
if(channels == 1){ |
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_MONO; |
||||||
|
}else if(channels == 2){ |
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_STEREO; |
||||||
|
}else{ |
||||||
|
channelConfig = AudioFormat.CHANNEL_OUT_STEREO; |
||||||
|
} |
||||||
|
|
||||||
|
int bufferSizeInBytes = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat); |
||||||
|
|
||||||
|
return new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, |
||||||
|
bufferSizeInBytes, AudioTrack.MODE_STREAM); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue