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
這篇文章關於ffmpeg的解決方案僅提供參考,若有錯誤,歡迎指正。ide
在音視頻播放的時候,咱們均是將讀取到的Packet放入到隊列中,而後再從隊列中取出packet進行解碼播放。因此在播放、暫停時能夠在隊列的取出上面作文章。函數
int FFmpegMusic::get(AVPacket *avPacket) { LOGE("取出隊列") pthread_mutex_lock(&mutex); while (isPlay){ if(!queue.empty()&&isPause){ LOGE("ispause %d",isPause); //若是隊列中有數據能夠拿出來 if(av_packet_ref(avPacket,queue.front())){ break; } //取成功了,彈出隊列,銷燬packet AVPacket *packet2 = queue.front(); queue.pop(); av_free(packet2); break; } else{ LOGE("音頻執行wait") LOGE("ispause %d",isPause); pthread_cond_wait(&cond,&mutex); } } pthread_mutex_unlock(&mutex); return 0; }
如上,咱們控制isPause的值來讓線程進入wait狀態,若是須要繼續播放的話需喚醒線程,繼續從隊列中取出packetspa
if(isPause==1){ isPause=0; } else{ isPause=1; pthread_cond_signal(&cond); }
進度條在java層使用的SeekBar,須要取到視頻的總時間以及播放的時間。
視頻總時間,注意是在獲取視頻信息(avformat_find_stream_info(pFormatCtx, NULL))事後才能獲得總時間:線程
//獲得播放總時間 if(pFormatCtx->duration != AV_NOPTS_VALUE){ duration = pFormatCtx->duration;//微秒 }
由於播放時是以音頻爲基礎,因此須要獲得音頻的播放時間,由於在播放音頻時有單獨記錄音頻的播放時間,因此咱們能夠直接獲得:code
return ffmpegMusic->clock;
在java層實時繪製取到音頻播放時間並繪製進度,while(true)的條件爲是否正在播放,這裏爲了方便,因此這樣寫:orm
new Thread(new Runnable() { @Override public void run() { while (true){ try { Message message = new Message(); message.what = (int) Player.getCurrentPosition()*1000; handler.sendMessage(message); } catch (Exception e) { e.printStackTrace(); } } } }).start(); private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); mSeekBar.setProgress(msg.what); } };
主要涉及到跳幀函數:int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags);
AVFormatContext *s:封裝格式上下文
int stream_index:音頻流或者視頻流
int64_t timestamp:時間,若是流爲-1,那麼時間的單位爲AV_TIME_BASE;若是爲具體的流,那麼時間單位爲AVStream.time_base 。
int flags:功能flag
大概思路:
清空隊列-->av_seek_frame-->av_read_frame-->put隊列
注意:由於系統自帶的隊列沒有clear函數,若是使用queue.pop將會使效率很是慢,會有卡頓跡象,因此做者將queue換成了vector,這樣能夠直接clear,在清空隊列的時候效率大大提升。視頻
void seekTo(int mesc) { if (mesc <= 0) { mesc=0; } //清空vector ffmpegMusic->queue.clear(); ffmpegVideo->queue.clear(); //跳幀 //-1表示默認流 if (av_seek_frame(pFormatCtx, -1, mesc * AV_TIME_BASE, AVSEEK_FLAG_BACKWARD) < 0) { LOGE("failed") } else { LOGE("success") } //若是是確認某一個流, av_seek_frame(pFormatCtx, ffmpegVideo->index, (int64_t) (mesc /av_q2d(ffmpegVideo->time_base)), AVSEEK_FLAG_BACKWARD); av_seek_frame(pFormatCtx, ffmpegMusic->index, (int64_t) (mesc /av_q2d(ffmpegMusic->time_base)), AVSEEK_FLAG_BACKWARD); } //while循環一直在線程中執行 while (isPlay) { ret = av_read_frame(pFormatCtx, packet); if (ret == 0) { if (ffmpegVideo && ffmpegVideo->isPlay && packet->stream_index == ffmpegVideo->index ) { //將視頻packet壓入隊列 ffmpegVideo->put(packet); } else if (ffmpegMusic && ffmpegMusic->isPlay && packet->stream_index == ffmpegMusic->index) { ffmpegMusic->put(packet); } av_packet_unref(packet); } else if (ret == AVERROR_EOF) { // 讀完了 //讀取完畢 可是不必定播放完畢 while (isPlay) { if (ffmpegVideo->queue.empty() && ffmpegMusic->queue.empty()) { break; } // LOGE("等待播放完成"); av_usleep(10000); } } }
須要跳轉時直接調用這個函數便可:隊列
seekTo(gettime/1000);//將毫秒轉爲秒