這個是FFmpeg解碼出音頻,給AudioTrack播放,這回纔算是java與c語言之間合做c++
這回咱們將會從c++裏調用java函數,下面就是關於c++使用AudioTrack的代碼git
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); } }
咱們再來看看c++的代碼github
首先註冊組件,而後獲得音頻流緩存
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; } }
獲取×××ide
//獲取×××上下文 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();
設置解碼出的聲音一系列的屬性,好比:單聲道、雙聲道、採集點大小、採集率,還能夠在這裏對聲音添加特效,工具
//獲得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);ui
swr_init(swrContext);
// 獲取通道數 2
int out_channer_nb = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
經過反射可以運行java函數
// 反射獲得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");
在一邊解碼的時候一邊給數據給AudioTrack播放
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);
}
}
}
釋放資源
av_frame_free(&frame); swr_free(&swrContext); avcodec_close(pCodecCtx); avformat_close_input(&pFormatCtx); env->ReleaseStringUTFChars(input_, input);
FFmpeg只是音視頻處理的工具,他沒有播放視頻和音頻的能力,因此咱們須要SurfaceView顯示視頻,AudioTrack播放聲音,並且OpenGLES也能播放聲音,這個後面說
下一次就是說如何將視頻的聲音給聽換掉,也就是將音視頻的解碼和編碼都來搞一次