Feature: add audio player class

pull/221/head
xufuji456 2 years ago
parent 25aa93600e
commit e8774a77d0
  1. 2
      app/src/main/cpp/CMakeLists.txt
  2. 58
      app/src/main/cpp/audio_player_jni.cpp
  3. 228
      app/src/main/cpp/ff_audio_player.cpp
  4. 57
      app/src/main/cpp/ff_audio_player.h

@ -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

@ -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…
Cancel
Save