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