parent
							
								
									638ccffda0
								
							
						
					
					
						commit
						31d19d6d0f
					
				@ -1,280 +0,0 @@ | 
				
			||||
//
 | 
				
			||||
// Created by frank on 2018/2/1.
 | 
				
			||||
//
 | 
				
			||||
#include <jni.h> | 
				
			||||
#include <cstdlib> | 
				
			||||
#include <unistd.h> | 
				
			||||
#include "visualizer/frank_visualizer.h" | 
				
			||||
 | 
				
			||||
#ifdef __cplusplus | 
				
			||||
extern "C" { | 
				
			||||
#endif | 
				
			||||
 | 
				
			||||
#include "libavformat/avformat.h" | 
				
			||||
#include "libavcodec/avcodec.h" | 
				
			||||
#include "libswscale/swscale.h" | 
				
			||||
#include "libswresample/swresample.h" | 
				
			||||
#include "libavfilter/buffersink.h" | 
				
			||||
#include "libavfilter/buffersrc.h" | 
				
			||||
#include "libavfilter/avfiltergraph.h" | 
				
			||||
#include "libavutil/opt.h" | 
				
			||||
#include "ffmpeg_jni_define.h" | 
				
			||||
 | 
				
			||||
#ifdef __cplusplus | 
				
			||||
} | 
				
			||||
#endif | 
				
			||||
 | 
				
			||||
#define TAG "AudioPlayer" | 
				
			||||
#define SLEEP_TIME (1000 * 16) | 
				
			||||
#define MAX_AUDIO_FRAME_SIZE (48000 * 4) | 
				
			||||
 | 
				
			||||
int filter_again = 0; | 
				
			||||
int filter_release = 0; | 
				
			||||
const char *filter_desc = "superequalizer=6b=4:8b=5:10b=5"; | 
				
			||||
FrankVisualizer *mVisualizer; | 
				
			||||
 | 
				
			||||
void fft_callback(JNIEnv *jniEnv, jobject thiz, jmethodID fft_method, int8_t* arg, int samples); | 
				
			||||
 | 
				
			||||
int init_equalizer_filter(const char *filter_description, AVCodecContext *codecCtx, AVFilterGraph **graph, | 
				
			||||
        AVFilterContext **src, AVFilterContext **sink) { | 
				
			||||
    int ret = 0; | 
				
			||||
    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(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(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, filter_description, | 
				
			||||
                                        &inputs, &outputs, nullptr)) < 0) { | 
				
			||||
        LOGE(TAG, "avfilter_graph_parse_ptr error:%d", ret); | 
				
			||||
        goto end; | 
				
			||||
    } | 
				
			||||
    if ((ret = avfilter_graph_config(filter_graph, nullptr)) < 0) { | 
				
			||||
        LOGE(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; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
AUDIO_PLAYER_FUNC(void, play111, jstring input_jstr, jstring filter_jstr) { | 
				
			||||
    int got_frame = 0, ret; | 
				
			||||
    AVPacket packet; | 
				
			||||
    AVFilterGraph *audioFilterGraph; | 
				
			||||
    AVFilterContext *audioSrcContext; | 
				
			||||
    AVFilterContext *audioSinkContext; | 
				
			||||
 | 
				
			||||
    const char *input_cstr = env->GetStringUTFChars(input_jstr, nullptr); | 
				
			||||
    LOGI(TAG, "input url=%s", input_cstr); | 
				
			||||
    filter_desc = env->GetStringUTFChars(filter_jstr, nullptr); | 
				
			||||
    LOGE(TAG, "filter_desc=%s", filter_desc); | 
				
			||||
    AVFormatContext *pFormatCtx = avformat_alloc_context(); | 
				
			||||
    if (avformat_open_input(&pFormatCtx, input_cstr, nullptr, nullptr) != 0) { | 
				
			||||
        LOGE(TAG, "Couldn't open the audio file!"); | 
				
			||||
        return; | 
				
			||||
    } | 
				
			||||
    if (avformat_find_stream_info(pFormatCtx, nullptr) < 0) { | 
				
			||||
        LOGE(TAG, "Couldn't find stream info!"); | 
				
			||||
        return; | 
				
			||||
    } | 
				
			||||
    int i = 0, audio_stream_idx = -1; | 
				
			||||
    for (; i < pFormatCtx->nb_streams; i++) { | 
				
			||||
        if (pFormatCtx->streams[i]->codecpar->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 == nullptr) { | 
				
			||||
        LOGE(TAG, "Couldn't find audio decoder!"); | 
				
			||||
        return; | 
				
			||||
    } | 
				
			||||
    if (avcodec_open2(codecCtx, codec, nullptr) < 0) { | 
				
			||||
        LOGE(TAG, "Couldn't open audio decoder"); | 
				
			||||
        return; | 
				
			||||
    } | 
				
			||||
    AVFrame *frame = av_frame_alloc(); | 
				
			||||
    SwrContext *swrCtx = swr_alloc(); | 
				
			||||
    enum AVSampleFormat in_sample_fmt = codecCtx->sample_fmt; | 
				
			||||
    enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16; | 
				
			||||
    int in_sample_rate = codecCtx->sample_rate; | 
				
			||||
    int out_sample_rate = in_sample_rate; | 
				
			||||
    uint64_t in_ch_layout = codecCtx->channel_layout; | 
				
			||||
    uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO; | 
				
			||||
 | 
				
			||||
    swr_alloc_set_opts(swrCtx, (int64_t)out_ch_layout, out_sample_fmt, out_sample_rate, | 
				
			||||
                       (int64_t)in_ch_layout, in_sample_fmt, in_sample_rate, 0, nullptr); | 
				
			||||
    swr_init(swrCtx); | 
				
			||||
    int out_channel = av_get_channel_layout_nb_channels(out_ch_layout); | 
				
			||||
    jclass player_class = env->GetObjectClass(thiz); | 
				
			||||
    if (!player_class) { | 
				
			||||
        LOGE(TAG, "player_class not found..."); | 
				
			||||
    } | 
				
			||||
    //get AudioTrack by reflection
 | 
				
			||||
    jmethodID audio_track_method = env->GetMethodID(player_class, "createAudioTrack", | 
				
			||||
            "(II)Landroid/media/AudioTrack;"); | 
				
			||||
    if (!audio_track_method) { | 
				
			||||
        LOGE(TAG, "audio_track_method not found..."); | 
				
			||||
    } | 
				
			||||
    jobject audio_track = env->CallObjectMethod(thiz, audio_track_method, out_sample_rate, out_channel); | 
				
			||||
    //call play method
 | 
				
			||||
    jclass audio_track_class = env->GetObjectClass(audio_track); | 
				
			||||
    jmethodID audio_track_play_mid = env->GetMethodID(audio_track_class, "play", "()V"); | 
				
			||||
    env->CallVoidMethod(audio_track, audio_track_play_mid); | 
				
			||||
    //get write method
 | 
				
			||||
    jmethodID audio_track_write_mid = env->GetMethodID(audio_track_class, "write", "([BII)I"); | 
				
			||||
    auto *out_buffer = (uint8_t *) av_malloc(MAX_AUDIO_FRAME_SIZE); | 
				
			||||
 | 
				
			||||
    /* Set up the filter graph. */ | 
				
			||||
    AVFrame *filter_frame = av_frame_alloc(); | 
				
			||||
    ret = init_equalizer_filter(filter_desc, codecCtx, &audioFilterGraph, &audioSrcContext, &audioSinkContext); | 
				
			||||
    if (ret < 0) { | 
				
			||||
        LOGE(TAG, "Unable to init filter graph:%d", ret); | 
				
			||||
    } | 
				
			||||
 | 
				
			||||
    jmethodID fft_method = env->GetMethodID(player_class, "fftCallbackFromJNI", "([B)V"); | 
				
			||||
 | 
				
			||||
    mVisualizer = new FrankVisualizer(); | 
				
			||||
    mVisualizer->init_visualizer(); | 
				
			||||
 | 
				
			||||
    //read audio frame
 | 
				
			||||
    while (av_read_frame(pFormatCtx, &packet) >= 0 && !filter_release) { | 
				
			||||
        if (packet.stream_index != audio_stream_idx) { | 
				
			||||
            av_packet_unref(&packet); | 
				
			||||
            continue; | 
				
			||||
        } | 
				
			||||
        if (filter_again) { | 
				
			||||
            filter_again = 0; | 
				
			||||
            avfilter_graph_free(&audioFilterGraph); | 
				
			||||
            if (init_equalizer_filter(filter_desc, codecCtx, &audioFilterGraph, &audioSrcContext, &audioSinkContext) < 0) { | 
				
			||||
                LOGE(TAG, "init_filter error, ret=%d\n", ret); | 
				
			||||
                goto end; | 
				
			||||
            } | 
				
			||||
            LOGE(TAG, "play again,filter_descr=_=%s", filter_desc); | 
				
			||||
        } | 
				
			||||
        ret = avcodec_decode_audio4(codecCtx, frame, &got_frame, &packet); | 
				
			||||
        if (ret < 0) { | 
				
			||||
            break; | 
				
			||||
        } | 
				
			||||
        if (got_frame > 0) { | 
				
			||||
 | 
				
			||||
            int nb_samples = frame->nb_samples < MAX_FFT_SIZE ? frame->nb_samples : MAX_FFT_SIZE; | 
				
			||||
            if (nb_samples >= MIN_FFT_SIZE) { | 
				
			||||
                int8_t *output_data = mVisualizer->fft_run(frame->data[0], nb_samples); | 
				
			||||
                fft_callback(env, thiz, fft_method, output_data, mVisualizer->getOutputSample()); | 
				
			||||
            } | 
				
			||||
 | 
				
			||||
            ret = av_buffersrc_add_frame(audioSrcContext, frame); | 
				
			||||
            if (ret < 0) { | 
				
			||||
                LOGE(TAG, "Error add the frame to the filter graph:%d", ret); | 
				
			||||
            } | 
				
			||||
            /* Get all the filtered output that is available. */ | 
				
			||||
            while (true) { | 
				
			||||
                ret = av_buffersink_get_frame(audioSinkContext, filter_frame); | 
				
			||||
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) | 
				
			||||
                    break; | 
				
			||||
                if (ret < 0) { | 
				
			||||
                    LOGE(TAG, "Error get the frame from the filter graph:%d", ret); | 
				
			||||
                    goto end; | 
				
			||||
                } | 
				
			||||
                //convert audio format
 | 
				
			||||
                swr_convert(swrCtx, &out_buffer, MAX_AUDIO_FRAME_SIZE, | 
				
			||||
                            (const uint8_t **) /*frame*/filter_frame->data, /*frame*/filter_frame->nb_samples); | 
				
			||||
                int out_buffer_size = av_samples_get_buffer_size(nullptr, out_channel, | 
				
			||||
                        /*frame*/filter_frame->nb_samples, out_sample_fmt, 1); | 
				
			||||
 | 
				
			||||
                jbyteArray audio_sample_array = env->NewByteArray(out_buffer_size); | 
				
			||||
                jbyte *sample_byte_array = env->GetByteArrayElements(audio_sample_array, nullptr); | 
				
			||||
                memcpy(sample_byte_array, out_buffer, (size_t) out_buffer_size); | 
				
			||||
                env->ReleaseByteArrayElements(audio_sample_array, sample_byte_array, 0); | 
				
			||||
                //call write method to play
 | 
				
			||||
                env->CallIntMethod(audio_track, audio_track_write_mid, | 
				
			||||
                                   audio_sample_array, 0, out_buffer_size); | 
				
			||||
                env->DeleteLocalRef(audio_sample_array); | 
				
			||||
                av_frame_unref(filter_frame); | 
				
			||||
                usleep(SLEEP_TIME); | 
				
			||||
            } | 
				
			||||
        } | 
				
			||||
        av_packet_unref(&packet); | 
				
			||||
    } | 
				
			||||
end: | 
				
			||||
    av_free(out_buffer); | 
				
			||||
    swr_free(&swrCtx); | 
				
			||||
    avfilter_graph_free(&audioFilterGraph); | 
				
			||||
//    avcodec_free_context(&codecCtx);
 | 
				
			||||
    avformat_close_input(&pFormatCtx); | 
				
			||||
    av_frame_free(&frame); | 
				
			||||
    av_frame_free(&filter_frame); | 
				
			||||
    env->ReleaseStringUTFChars(input_jstr, input_cstr); | 
				
			||||
    env->ReleaseStringUTFChars(filter_jstr, filter_desc); | 
				
			||||
    jmethodID releaseMethod = env->GetMethodID(player_class, "releaseAudioTrack", "()V"); | 
				
			||||
    env->CallVoidMethod(thiz, releaseMethod); | 
				
			||||
    filter_again = 0; | 
				
			||||
    filter_release = 0; | 
				
			||||
    mVisualizer->release_visualizer(); | 
				
			||||
    LOGE(TAG, "audio release..."); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
AUDIO_PLAYER_FUNC(void, again111, jstring filter_jstr) { | 
				
			||||
    if (!filter_jstr) return; | 
				
			||||
    filter_again = 1; | 
				
			||||
    filter_desc = env->GetStringUTFChars(filter_jstr, nullptr); | 
				
			||||
} | 
				
			||||
 | 
				
			||||
AUDIO_PLAYER_FUNC(void, release111) { | 
				
			||||
    filter_release = 1; | 
				
			||||
} | 
				
			||||
 | 
				
			||||
void fft_callback(JNIEnv *jniEnv, jobject thiz, jmethodID fft_method, int8_t * arg, int samples) { | 
				
			||||
    jbyteArray dataArray = jniEnv->NewByteArray(samples); | 
				
			||||
    jniEnv->SetByteArrayRegion(dataArray, 0, samples, arg); | 
				
			||||
    jniEnv->CallVoidMethod(thiz, fft_method, dataArray); | 
				
			||||
    jniEnv->DeleteLocalRef(dataArray); | 
				
			||||
} | 
				
			||||
					Loading…
					
					
				
		Reference in new issue