Android使用FFmpeg(一)--編譯ffmpeg
Android使用FFmpeg(二)--Android Studio配置ffmpeg
Android使用FFmpeg(三)--ffmpeg實現視頻播放
Android使用FFmpeg(四)--ffmpeg實現音頻播放(使用AudioTrack進行播放)
Android使用FFmpeg(五)--ffmpeg實現音頻播放(使用openSL ES進行播放)
Android使用FFmpeg(六)--ffmpeg實現音視頻同步播放
Android使用FFmpeg(七)--ffmpeg實現暫停、快退快進播放java
jni調用Java方法
AudioTrack使用
關於音頻git
和視頻播放同樣咱們依照流程圖來實現,從這個流程圖能夠看出,音頻播放和視頻播放的大概流程並無多大差異,只是細節之處須要處理。github
1.註冊組件,打開音頻文件並獲取內容,找到音頻流:緩存
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,裝解碼前的數據 AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket)); //申請avframe,裝解碼後的數據 AVFrame *frame = av_frame_alloc();
3.初始化SwrContext,進行重採樣post
//獲得SwrContext ,進行重採樣,具體參考http://blog.csdn.net/jammg/article/details/52688506 SwrContext *swrContext = swr_alloc(); //緩存區 uint8_t *out_buffer = (uint8_t *) av_malloc(44100 * 2); //輸出的聲道佈局(立體聲) uint64_t out_ch_layout=AV_CH_LAYOUT_STEREO; //輸出採樣位數 16位 enum AVSampleFormat out_formart=AV_SAMPLE_FMT_S16; //輸出的採樣率必須與輸入相同 int out_sample_rate = pCodecCtx->sample_rate; //swr_alloc_set_opts將PCM源文件的採樣格式轉換爲本身但願的採樣格式 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循環讀取內容,並經過AudioTrack進行播放:ui
// 獲取通道數 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"); // 反射調用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); // 緩衝區的大小 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);
jni經過調用java層的audiotrack方法來實現播放,可是java層的audiotrack方法又是經過調用底層的openesl es來進行播放,至關於繞了一個圈,因此在下篇文章中將實現使用opensl es來直接播放音頻。spa
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進行調用 public void createTrack(int sampleRateInHz,int nb_channals) { int channaleConfig;//通道數 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傳入音頻數據 public void playTrack(byte[] buffer, int lenth) { if (audioTrack != null && audioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) { audioTrack.write(buffer, 0, lenth); } } }
按照流程,使用AudioTrack播放音頻就是這麼回事。經過和上篇的播放視頻相比較,流程就差很少了:註冊->解封裝->獲取流->解碼->播放。
源碼地址.net