parent
25aa93600e
commit
e8774a77d0
@ -0,0 +1,58 @@ |
|||||||
|
//
|
||||||
|
// Created by xu fulong on 2022/9/4.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "ff_audio_player.h" |
||||||
|
#include <jni.h> |
||||||
|
#include <unistd.h> |
||||||
|
|
||||||
|
#define SLEEP_TIME (16000) |
||||||
|
|
||||||
|
AUDIO_PLAYER_FUNC(void, play, jstring path) { |
||||||
|
if (path == nullptr) |
||||||
|
return; |
||||||
|
|
||||||
|
int result = 0; |
||||||
|
const char* native_path = env->GetStringUTFChars(path, JNI_FALSE); |
||||||
|
auto *audioPlayer = new FFAudioPlayer(); |
||||||
|
// 打开输入流
|
||||||
|
audioPlayer->open(native_path); |
||||||
|
// 初始化AudioTrack
|
||||||
|
jclass audio_class = env->GetObjectClass(thiz); |
||||||
|
jmethodID audio_track_method = env->GetMethodID(audio_class, |
||||||
|
"createAudioTrack", "(II)Landroid/media/AudioTrack;"); |
||||||
|
jobject audio_track = env->CallObjectMethod(thiz, audio_track_method, audioPlayer->getSampleRate(), audioPlayer->getChannel()); |
||||||
|
// 调用play函数
|
||||||
|
jclass audio_track_class = env->GetObjectClass(audio_track); |
||||||
|
jmethodID play_method = env->GetMethodID(audio_track_class, "play", "()V"); |
||||||
|
env->CallVoidMethod(audio_track, play_method); |
||||||
|
// 获取write方法id
|
||||||
|
jmethodID write_method = env->GetMethodID(audio_track_class, "write", "([BII)I"); |
||||||
|
|
||||||
|
// 解码音频zhen
|
||||||
|
while (result >= 0) { |
||||||
|
result = audioPlayer->decodeAudio(); |
||||||
|
if (result == 0) { |
||||||
|
continue; |
||||||
|
} else if (result < 0) { |
||||||
|
break; |
||||||
|
} |
||||||
|
int size = result; |
||||||
|
// 调用AudioTrack播放(可优化:数组复用)
|
||||||
|
jbyteArray audio_array = env->NewByteArray(size); |
||||||
|
jbyte *data_address = env->GetByteArrayElements(audio_array, JNI_FALSE); |
||||||
|
memcpy(data_address, audioPlayer->getDecodeFrame(), size); |
||||||
|
env->ReleaseByteArrayElements(audio_array, data_address, 0); |
||||||
|
env->CallIntMethod(audio_track, write_method, audio_array, 0, size); |
||||||
|
env->DeleteLocalRef(audio_array); |
||||||
|
|
||||||
|
// 延时等待
|
||||||
|
usleep(SLEEP_TIME); |
||||||
|
} |
||||||
|
|
||||||
|
env->ReleaseStringUTFChars(path, native_path); |
||||||
|
jmethodID release_method = env->GetMethodID(audio_class, "releaseAudioTrack", "()V"); |
||||||
|
env->CallVoidMethod(thiz, release_method); |
||||||
|
audioPlayer->close(); |
||||||
|
delete audioPlayer; |
||||||
|
} |
@ -0,0 +1,228 @@ |
|||||||
|
//
|
||||||
|
// Created by xu fulong on 2022/9/4.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "ff_audio_player.h" |
||||||
|
|
||||||
|
#define AUDIO_TAG "AudioPlayer" |
||||||
|
#define BUFFER_SIZE (48000 * 10) |
||||||
|
|
||||||
|
const char *FILTER_DESC = "superequalizer=6b=4:8b=5:10b=5"; |
||||||
|
|
||||||
|
int initFilter(const char *filterDesc, AVCodecContext *codecCtx, AVFilterGraph **graph, |
||||||
|
AVFilterContext **src, AVFilterContext **sink) { |
||||||
|
int ret; |
||||||
|
char args[512]; |
||||||
|
AVFilterContext *buffersrc_ctx; |
||||||
|
AVFilterContext *buffersink_ctx; |
||||||
|
AVRational time_base = codecCtx->time_base; |
||||||
|
AVFilterInOut *inputs = avfilter_inout_alloc(); |
||||||
|
AVFilterInOut *outputs = avfilter_inout_alloc(); |
||||||
|
const AVFilter *buffersrc = avfilter_get_by_name("abuffer"); |
||||||
|
const AVFilter *buffersink = avfilter_get_by_name("abuffersink"); |
||||||
|
|
||||||
|
AVFilterGraph *filter_graph = avfilter_graph_alloc(); |
||||||
|
if (!outputs || !inputs || !filter_graph) { |
||||||
|
ret = AVERROR(ENOMEM); |
||||||
|
goto end; |
||||||
|
} |
||||||
|
|
||||||
|
/* buffer audio source: the decoded frames from the decoder will be inserted here. */ |
||||||
|
if (!codecCtx->channel_layout) |
||||||
|
codecCtx->channel_layout = static_cast<uint64_t>(av_get_default_channel_layout( |
||||||
|
codecCtx->channels)); |
||||||
|
snprintf(args, sizeof(args), |
||||||
|
"time_base=%d/%d:sample_rate=%d:sample_fmt=%s:channel_layout=0x%" PRIx64 "", |
||||||
|
time_base.num, time_base.den, codecCtx->sample_rate, |
||||||
|
av_get_sample_fmt_name(codecCtx->sample_fmt), codecCtx->channel_layout); |
||||||
|
|
||||||
|
ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", |
||||||
|
args, nullptr, filter_graph); |
||||||
|
if (ret < 0) { |
||||||
|
LOGE(AUDIO_TAG, "Cannot create buffer source:%d", ret); |
||||||
|
goto end; |
||||||
|
} |
||||||
|
/* buffer audio sink: to terminate the filter chain. */ |
||||||
|
ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", |
||||||
|
nullptr, nullptr, filter_graph); |
||||||
|
if (ret < 0) { |
||||||
|
LOGE(AUDIO_TAG, "Cannot create buffer sink:%d", ret); |
||||||
|
goto end; |
||||||
|
} |
||||||
|
|
||||||
|
outputs->name = av_strdup("in"); |
||||||
|
outputs->filter_ctx = buffersrc_ctx; |
||||||
|
outputs->pad_idx = 0; |
||||||
|
outputs->next = nullptr; |
||||||
|
inputs->name = av_strdup("out"); |
||||||
|
inputs->filter_ctx = buffersink_ctx; |
||||||
|
inputs->pad_idx = 0; |
||||||
|
inputs->next = nullptr; |
||||||
|
|
||||||
|
if ((ret = avfilter_graph_parse_ptr(filter_graph, filterDesc, |
||||||
|
&inputs, &outputs, nullptr)) < 0) { |
||||||
|
LOGE(AUDIO_TAG, "avfilter_graph_parse_ptr error:%d", ret); |
||||||
|
goto end; |
||||||
|
} |
||||||
|
if ((ret = avfilter_graph_config(filter_graph, nullptr)) < 0) { |
||||||
|
LOGE(AUDIO_TAG, "avfilter_graph_config error:%d", ret); |
||||||
|
goto end; |
||||||
|
} |
||||||
|
*graph = filter_graph; |
||||||
|
*src = buffersrc_ctx; |
||||||
|
*sink = buffersink_ctx; |
||||||
|
end: |
||||||
|
avfilter_inout_free(&inputs); |
||||||
|
avfilter_inout_free(&outputs); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
int FFAudioPlayer::open(const char *path) { |
||||||
|
if (!path) |
||||||
|
return -1; |
||||||
|
|
||||||
|
int ret; |
||||||
|
const AVCodec *codec; |
||||||
|
inputFrame = av_frame_alloc(); |
||||||
|
packet = av_packet_alloc(); |
||||||
|
out_buffer = new uint8_t [BUFFER_SIZE]; |
||||||
|
|
||||||
|
// 打开输入流
|
||||||
|
ret = avformat_open_input(&formatContext, path, nullptr, nullptr); |
||||||
|
if (ret < 0) { |
||||||
|
LOGE(AUDIO_TAG, "avformat_open_input error=%s", av_err2str(ret)); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
// 查找stream信息
|
||||||
|
avformat_find_stream_info(formatContext, nullptr); |
||||||
|
// 找到音频index
|
||||||
|
for (int i=0; i<formatContext->nb_streams; i++) { |
||||||
|
if (AVMEDIA_TYPE_AUDIO == formatContext->streams[i]->codecpar->codec_type) { |
||||||
|
audio_index = i; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
if (audio_index == -1) { |
||||||
|
return -1; |
||||||
|
} |
||||||
|
// 找到codec
|
||||||
|
codec = avcodec_find_decoder(formatContext->streams[audio_index]->codecpar->codec_id); |
||||||
|
codecContext = avcodec_alloc_context3(codec); |
||||||
|
avcodec_parameters_to_context(codecContext, formatContext->streams[audio_index]->codecpar); |
||||||
|
// 打开解码器
|
||||||
|
ret = avcodec_open2(codecContext, codec, nullptr); |
||||||
|
if (ret < 0) { |
||||||
|
LOGE(AUDIO_TAG, "avcodec_open2 error=%s", av_err2str(ret)); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
// 输入:采样率、声道布局、音频格式
|
||||||
|
int in_sample_rate = codecContext->sample_rate; |
||||||
|
auto in_sample_fmt = codecContext->sample_fmt; |
||||||
|
int in_ch_layout = (int)codecContext->channel_layout; |
||||||
|
out_sample_rate = in_sample_rate; // 输出采样率等于输入采样率
|
||||||
|
out_sample_fmt = AV_SAMPLE_FMT_S16; // 16位
|
||||||
|
out_ch_layout = AV_CH_LAYOUT_STEREO; // 双声道
|
||||||
|
out_channel = codecContext->channels; |
||||||
|
// 初始化音频格式转换上下文swrContext
|
||||||
|
swrContext = swr_alloc(); |
||||||
|
swr_alloc_set_opts(swrContext, out_ch_layout, out_sample_fmt, out_sample_rate, |
||||||
|
in_ch_layout, in_sample_fmt, in_sample_rate, 0, nullptr); |
||||||
|
swr_init(swrContext); |
||||||
|
|
||||||
|
filterFrame = av_frame_alloc(); |
||||||
|
initFilter(FILTER_DESC, codecContext, &audioFilterGraph, |
||||||
|
&audioSrcContext, &audioSinkContext); |
||||||
|
|
||||||
|
return 0; |
||||||
|
} |
||||||
|
|
||||||
|
int FFAudioPlayer::getChannel() const { |
||||||
|
return out_channel; |
||||||
|
} |
||||||
|
|
||||||
|
int FFAudioPlayer::getSampleRate() const { |
||||||
|
return out_sample_rate; |
||||||
|
} |
||||||
|
|
||||||
|
int FFAudioPlayer::decodeAudio() { |
||||||
|
int ret; |
||||||
|
// 读取音频数据(解封装)
|
||||||
|
ret = av_read_frame(formatContext, packet); |
||||||
|
if (ret < 0) { |
||||||
|
return ret; |
||||||
|
} |
||||||
|
// 判断是否位音频帧
|
||||||
|
if (packet->stream_index != audio_index) { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
// 解码音频帧
|
||||||
|
ret = avcodec_send_packet(codecContext, packet); |
||||||
|
if (ret < 0) { |
||||||
|
LOGE(AUDIO_TAG, "avcodec_send_packet=%s", av_err2str(ret)); |
||||||
|
} |
||||||
|
ret = avcodec_receive_frame(codecContext, inputFrame); |
||||||
|
if (ret < 0) { |
||||||
|
if (ret == AVERROR(EAGAIN)) { |
||||||
|
return 0; |
||||||
|
} else { |
||||||
|
return ret; |
||||||
|
} |
||||||
|
} |
||||||
|
// put into filter
|
||||||
|
ret = av_buffersrc_add_frame(audioSrcContext, inputFrame); |
||||||
|
if (ret < 0) { |
||||||
|
LOGE(AUDIO_TAG, "av_buffersrc_add_frame error=%s", av_err2str(ret)); |
||||||
|
} |
||||||
|
// drain from filter
|
||||||
|
ret = av_buffersink_get_frame(audioSinkContext, filterFrame); |
||||||
|
if (ret == AVERROR(EAGAIN)) { |
||||||
|
return 0; |
||||||
|
} else if (ret == AVERROR_EOF) { |
||||||
|
LOGE(AUDIO_TAG, "enf of stream..."); |
||||||
|
return ret; |
||||||
|
} else if (ret < 0) { |
||||||
|
LOGE(AUDIO_TAG, "av_buffersink_get_frame error:%s", av_err2str(ret)); |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
// 音频格式转换
|
||||||
|
swr_convert(swrContext, &out_buffer, BUFFER_SIZE, |
||||||
|
(const uint8_t **)(filterFrame->data), filterFrame->nb_samples); |
||||||
|
// 获取转换后缓冲区大小
|
||||||
|
int buffer_size = av_samples_get_buffer_size(nullptr, out_channel, |
||||||
|
filterFrame->nb_samples, out_sample_fmt, 1); |
||||||
|
LOGI(AUDIO_TAG, "buffer_size=%d", buffer_size); |
||||||
|
av_frame_unref(inputFrame); |
||||||
|
av_frame_unref(filterFrame); |
||||||
|
av_packet_unref(packet); |
||||||
|
return buffer_size; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
uint8_t *FFAudioPlayer::getDecodeFrame() const { |
||||||
|
return out_buffer; |
||||||
|
} |
||||||
|
|
||||||
|
void FFAudioPlayer::close() { |
||||||
|
if (formatContext) { |
||||||
|
avformat_close_input(&formatContext); |
||||||
|
} |
||||||
|
if (codecContext) { |
||||||
|
avcodec_free_context(&codecContext); |
||||||
|
} |
||||||
|
if (packet) { |
||||||
|
av_packet_free(&packet); |
||||||
|
} |
||||||
|
if (inputFrame) { |
||||||
|
av_frame_free(&inputFrame); |
||||||
|
} |
||||||
|
if (swrContext) { |
||||||
|
swr_close(swrContext); |
||||||
|
} |
||||||
|
avfilter_free(audioSrcContext); |
||||||
|
avfilter_free(audioSinkContext); |
||||||
|
if (audioFilterGraph) { |
||||||
|
avfilter_graph_free(&audioFilterGraph); |
||||||
|
} |
||||||
|
delete[] out_buffer; |
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
//
|
||||||
|
// Created by xu fulong on 2022/9/4.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef LEARNINGMEDIA_FF_AUDIO_PLAYER_H |
||||||
|
#define LEARNINGMEDIA_FF_AUDIO_PLAYER_H |
||||||
|
|
||||||
|
#include "ffmpeg_jni_define.h" |
||||||
|
|
||||||
|
#ifdef __cplusplus |
||||||
|
extern "C" { |
||||||
|
#endif |
||||||
|
#include "libavformat/avformat.h" |
||||||
|
#include "libavcodec/avcodec.h" |
||||||
|
#include "libswresample/swresample.h" |
||||||
|
|
||||||
|
#include "libavfilter/buffersink.h" |
||||||
|
#include "libavfilter/buffersrc.h" |
||||||
|
#include "libavfilter/avfiltergraph.h" |
||||||
|
#ifdef __cplusplus |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
class FFAudioPlayer { |
||||||
|
private: |
||||||
|
|
||||||
|
AVFormatContext *formatContext; |
||||||
|
AVCodecContext *codecContext; |
||||||
|
int audio_index = -1; |
||||||
|
SwrContext *swrContext; |
||||||
|
int out_sample_rate; |
||||||
|
int out_ch_layout; |
||||||
|
int out_channel; |
||||||
|
enum AVSampleFormat out_sample_fmt; |
||||||
|
AVPacket *packet; |
||||||
|
AVFrame *inputFrame; |
||||||
|
AVFrame *filterFrame; |
||||||
|
uint8_t *out_buffer; |
||||||
|
|
||||||
|
AVFilterGraph *audioFilterGraph; |
||||||
|
AVFilterContext *audioSrcContext; |
||||||
|
AVFilterContext *audioSinkContext; |
||||||
|
|
||||||
|
public: |
||||||
|
int open(const char* path); |
||||||
|
|
||||||
|
int getSampleRate() const; |
||||||
|
|
||||||
|
int getChannel() const; |
||||||
|
|
||||||
|
int decodeAudio(); |
||||||
|
|
||||||
|
uint8_t *getDecodeFrame() const; |
||||||
|
|
||||||
|
void close(); |
||||||
|
}; |
||||||
|
#endif //LEARNINGMEDIA_FF_AUDIO_PLAYER_H
|
Loading…
Reference in new issue