Android使用FFmpeg(四)--ffmpeg實(shí)現(xiàn)音頻播放(使用AudioTrack進(jìn)行播放)

關(guān)于

Android使用FFmpeg(一)--編譯ffmpeg
Android使用FFmpeg(二)--Android Studio配置ffmpeg
Android使用FFmpeg(三)--ffmpeg實(shí)現(xiàn)視頻播放
Android使用FFmpeg(四)--ffmpeg實(shí)現(xiàn)音頻播放(使用AudioTrack進(jìn)行播放)
Android使用FFmpeg(五)--ffmpeg實(shí)現(xiàn)音頻播放(使用openSL ES進(jìn)行播放)
Android使用FFmpeg(六)--ffmpeg實(shí)現(xiàn)音視頻同步播放
Android使用FFmpeg(七)--ffmpeg實(shí)現(xiàn)暫停、快退快進(jìn)播放

準(zhǔn)備工作

jni調(diào)用Java方法
AudioTrack使用
關(guān)于音頻

正文

ffmpeg實(shí)現(xiàn)音頻播放大概流程圖.png

和視頻播放一樣我們依照流程圖來實(shí)現(xiàn),從這個流程圖可以看出,音頻播放和視頻播放的大概流程并沒有多大差別,只是細(xì)節(jié)之處需要處理。

依照流程來實(shí)現(xiàn)

1.注冊組件,打開音頻文件并獲取內(nèi)容,找到音頻流:

av_register_all();
    AVFormatContext *pFormatCtx = avformat_alloc_context();
    //open
    if (avformat_open_input(&pFormatCtx, input, NULL, NULL) != 0) {
        LOGE("%s","打開輸入視頻文件失敗");
        return;
    }
    //獲取視頻信息
    if(avformat_find_stream_info(pFormatCtx,NULL) < 0){
        LOGE("%s","獲取視頻信息失敗");
        return;
    }
    int audio_stream_idx=-1;
    int i=0;
    for (int i = 0; i < pFormatCtx->nb_streams; ++i) {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
            LOGE("  找到音頻id %d", pFormatCtx->streams[i]->codec->codec_type);
            audio_stream_idx=i;
            break;
        }
    }

2.獲取解碼器,申請avframe和avpacket:

 //獲取解碼器上下文
    AVCodecContext *pCodecCtx=pFormatCtx->streams[audio_stream_idx]->codec;
    //獲取解碼器
    AVCodec *pCodex = avcodec_find_decoder(pCodecCtx->codec_id);
    //打開解碼器
    if (avcodec_open2(pCodecCtx, pCodex, NULL)<0) {
    }
    //申請avpakcet,裝解碼前的數(shù)據(jù)
    AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));
    //申請avframe,裝解碼后的數(shù)據(jù)
    AVFrame *frame = av_frame_alloc();

3.初始化SwrContext,進(jìn)行重采樣

//得到SwrContext ,進(jìn)行重采樣,具體參考http://blog.csdn.net/jammg/article/details/52688506
    SwrContext *swrContext = swr_alloc();
    //緩存區(qū)
    uint8_t *out_buffer = (uint8_t *) av_malloc(44100 * 2);
//輸出的聲道布局(立體聲)
    uint64_t  out_ch_layout=AV_CH_LAYOUT_STEREO;
//輸出采樣位數(shù)  16位
    enum AVSampleFormat out_formart=AV_SAMPLE_FMT_S16;
//輸出的采樣率必須與輸入相同
    int out_sample_rate = pCodecCtx->sample_rate;

//swr_alloc_set_opts將PCM源文件的采樣格式轉(zhuǎn)換為自己希望的采樣格式
    swr_alloc_set_opts(swrContext, out_ch_layout, out_formart, out_sample_rate,
                       pCodecCtx->channel_layout, pCodecCtx->sample_fmt, pCodecCtx->sample_rate, 0,
                       NULL);
    swr_init(swrContext);

4.通過while循環(huán)讀取內(nèi)容,并通過AudioTrack進(jìn)行播放:

//    獲取通道數(shù)  2
    int out_channer_nb = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
//    反射得到Class類型
    jclass david_player = env->GetObjectClass(instance);
//    反射得到createAudio方法
    jmethodID createAudio = env->GetMethodID(david_player, "createTrack", "(II)V");
//    反射調(diào)用createAudio
    env->CallVoidMethod(instance, createAudio, 44100, out_channer_nb);
    jmethodID audio_write = env->GetMethodID(david_player, "playTrack", "([BI)V");
int got_frame;
    while (av_read_frame(pFormatCtx, packet) >= 0) {
        if (packet->stream_index == audio_stream_idx) {
//            解碼  mp3   編碼格式frame----pcm   frame
            avcodec_decode_audio4(pCodecCtx, frame, &got_frame, packet);
            if (got_frame) {
                LOGE("解碼");
                swr_convert(swrContext, &out_buffer, 44100 * 2, (const uint8_t **) frame->data, frame->nb_samples);
//                緩沖區(qū)的大小
                int size = av_samples_get_buffer_size(NULL, out_channer_nb, frame->nb_samples,
                                                      AV_SAMPLE_FMT_S16, 1);
                jbyteArray audio_sample_array = env->NewByteArray(size);
                env->SetByteArrayRegion(audio_sample_array, 0, size, (const jbyte *) out_buffer);
                env->CallVoidMethod(instance, audio_write, audio_sample_array, size);
                env->DeleteLocalRef(audio_sample_array);
            }
        }
    }

5.釋放需要釋放的資源:

    av_frame_free(&frame);
    swr_free(&swrContext);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);
    env->ReleaseStringUTFChars(input_, input);

java層創(chuàng)建AudioTrack方法。

jni通過調(diào)用java層的audiotrack方法來實(shí)現(xiàn)播放,但是java層的audiotrack方法又是通過調(diào)用底層的openesl es來進(jìn)行播放,相當(dāng)于繞了一個圈,所以在下篇文章中將實(shí)現(xiàn)使用opensl es來直接播放音頻。

public class MusicPlay {
    static{
        System.loadLibrary("avcodec-56");
        System.loadLibrary("avdevice-56");
        System.loadLibrary("avfilter-5");
        System.loadLibrary("avformat-56");
        System.loadLibrary("avutil-54");
        System.loadLibrary("postproc-53");
        System.loadLibrary("swresample-1");
        System.loadLibrary("swscale-3");
        System.loadLibrary("native-lib");
    }
    public native void playSound(String input);

    private AudioTrack audioTrack;
    //    這個方法  是C進(jìn)行調(diào)用 
    public void createTrack(int sampleRateInHz,int nb_channals) {
        int channaleConfig;//通道數(shù)
        if (nb_channals == 1) {
            channaleConfig = AudioFormat.CHANNEL_OUT_MONO;
        } else if (nb_channals == 2) {
            channaleConfig = AudioFormat.CHANNEL_OUT_STEREO;
        }else {
            channaleConfig = AudioFormat.CHANNEL_OUT_MONO;
        }
        int buffersize=AudioTrack.getMinBufferSize(sampleRateInHz,
                channaleConfig, AudioFormat.ENCODING_PCM_16BIT);
        audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC,sampleRateInHz,channaleConfig,
                AudioFormat.ENCODING_PCM_16BIT,buffersize,AudioTrack.MODE_STREAM);
        audioTrack.play();
    }
    //C傳入音頻數(shù)據(jù)
    public void playTrack(byte[] buffer, int lenth) {
        if (audioTrack != null && audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
            audioTrack.write(buffer, 0, lenth);
        }
    }
}

小結(jié)

按照流程,使用AudioTrack播放音頻就是這么回事。通過和上篇的播放視頻相比較,流程就差不多了:注冊->解封裝->獲取流->解碼->播放。
源碼地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容