diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index 9a35a00..30371d9 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -46,6 +46,8 @@ add_library( # Sets the name of the library. ff_audio_resample.cpp pcm/pcm_process.cpp common_media_jni.cpp + ff_audio_player.cpp + audio_player_jni.cpp ) add_library( ffmpeg diff --git a/app/src/main/cpp/audio_player_jni.cpp b/app/src/main/cpp/audio_player_jni.cpp new file mode 100644 index 0000000..dcffe05 --- /dev/null +++ b/app/src/main/cpp/audio_player_jni.cpp @@ -0,0 +1,58 @@ +// +// Created by xu fulong on 2022/9/4. +// + +#include "ff_audio_player.h" +#include +#include + +#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; +} \ No newline at end of file diff --git a/app/src/main/cpp/ff_audio_player.cpp b/app/src/main/cpp/ff_audio_player.cpp new file mode 100644 index 0000000..f124a7d --- /dev/null +++ b/app/src/main/cpp/ff_audio_player.cpp @@ -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(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; inb_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; +} \ No newline at end of file diff --git a/app/src/main/cpp/ff_audio_player.h b/app/src/main/cpp/ff_audio_player.h new file mode 100644 index 0000000..62d2024 --- /dev/null +++ b/app/src/main/cpp/ff_audio_player.h @@ -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