htc e9+plus
htc derise 820( 錄影加截圖)
影片是錄製結果,聲音是透過麥克風會有一些雜音,效果還可以比MediaRecorder好很多
剛開始htc e9+跑很OK,換到htc derise 820 MediaMuxer.stop()會崩貴,和presentationTimeUs error 錄製就中斷,弄好幾天,終於解掉錯誤正常了
剛開始htc e9+跑很OK,換到htc derise 820 MediaMuxer.stop()會崩貴,和presentationTimeUs error 錄製就中斷,弄好幾天,終於解掉錯誤正常了
MediaCodec解碼器流程
會寫程式,應該都知道解碼器一定必須要用執行緒Thread去跑
public void run()
{
//解碼器
while(true){………}
}
1.
使用者授權取得mMediaProjection後
2.
MediaCodec定義格式preparerecorder取得surface,建立VirtualDisplay
3解碼器流程
while (!mQuit.get()) {
//根據MediaCodec,dequeueOutputBuffer() 傳回整數值
int index = mMediaCodec.dequeueOutputBuffer(mBufferInfo, TIMEOUT_US);
//後續輸出格式產生變化,譬如放音樂,暫停音樂,
if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
//重設格式resoutputform
at
} else
//INFO_TRY_AGAIN_LATER指請求超時
if (index == MediaCodec.INFO_TRY_AGAIN_LATER) {
等待一下sleep
} else
//大於0有效輸出
if (index >= 0) {
開始解碼encode
mMediaCodec.releaseOutputBuffer(index, false);//釋放輸出buffer
mMediaCodec.releaseOutputBuffer(index, false);//釋放輸出buffer
}
}
release();//最後一定要釋放掉,否則格式會錯
}
從暫存器MediaCodec.dequeueOutputBuffer()會傳回整數值,去判斷資料內容,會有三種狀況,當一組buffer處理完就要釋放releaseOutputBuffer(index, false),在處理下 一組
A.整數值MediaCodec.INFO_OUTPUT_FORMAT_CHANGED代表輸出格式產生變化
B.整數值MediaCodec.INFO_TRY_AGAIN_LATER代表請求超時
C.整數值大於等於0代表有效輸出
處理A狀況:
需要重設MediaCodec,使用getOutputFormat()重設,此使就可addTrack給MediaMuxer後續做寫入動作
MediaFormat newFormat = mMediaCodec.getOutputFormat(); mVideoTrackIndex = mMediaMuxer.addTrack(newFormat);
MediaFormat newFormat = mMediaCodec.getOutputFormat(); mVideoTrackIndex = mMediaMuxer.addTrack(newFormat);
mMediaMuxer.start();
處理B狀況:
等待一下sleep就可
處理C狀況:
把整數值餵給解碼器,開始解碼
為什會發生A狀況輸出格式產生變化,舉個例當解碼時使用者暫停或播放音樂,輸出格式就會產生變化
輸出格式產生變化,為什要重設格式,輸出格式產生變化getOutputFormat()會不一樣,
若不getOutputFormat()加入MediaMuxer add track,資料是寫不進去的,會產生error
4.至於後續解碼器根據索引值index整數值,取得buffer dat,這裡沒列出來,這部分較複雜,網路上有例子,影像解碼器比較單純只處理OUTPUT端,INPUT端 已交給VirtualDisplay,會些程式的,應該都看得懂,聲音解碼器須用另一個Thread去repeat流程
MediaCodec原理
1.
MediaCodec有一輸出端和輸入輸入端共用一組Buffer
2.
repeat流程
開始先準備給輸入端的buffer data,也就是採樣,譬如從麥克風或SPEAKER採樣
準備好輸入端buffer data,就開始第一次buffer data丟給MediaCodec,首先向MediaCodec申請一組空的buffer,把準備好輸入端buffer data添進申請的buffer,再把buffer送回MediaCodec, MediaCodec就會處理,然後MediaCodec處理資料添進這buffer送到輸出端,使用者拿到buffeer data,處理完資料,再把這組buffer送回MediaCodec然後release,這樣就完成一回INPUT和OUTPUT,只要一直重複這動作,這裡只介紹解碼流程
MediaCodec根據機器不同,會有不同狀況發生,A手機跑沒問題,B手機可能有問題,就要解BUG
解碼器流程關鍵代碼
MediaCodec原理
1.
MediaCodec有一輸出端和輸入輸入端共用一組Buffer
2.
repeat流程
開始先準備給輸入端的buffer data,也就是採樣,譬如從麥克風或SPEAKER採樣
準備好輸入端buffer data,就開始第一次buffer data丟給MediaCodec,首先向MediaCodec申請一組空的buffer,把準備好輸入端buffer data添進申請的buffer,再把buffer送回MediaCodec, MediaCodec就會處理,然後MediaCodec處理資料添進這buffer送到輸出端,使用者拿到buffeer data,處理完資料,再把這組buffer送回MediaCodec然後release,這樣就完成一回INPUT和OUTPUT,只要一直重複這動作,這裡只介紹解碼流程
MediaCodec根據機器不同,會有不同狀況發生,A手機跑沒問題,B手機可能有問題,就要解BUG
解碼器流程關鍵代碼
public class surface_mediacodec extends Thread {
………………………….
@Override
public void run()
{
startRecord();//開始錄屏
}
public void startRecord() {
preparerecorder();
//設置MediaCodec video
//preapareaudioencoder();
//設置MediaCodec audio
vp = mp.createVirtualDisplay("record_screen", mwindowWidth, mwindowHeight, mscreendi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mSurface, null, null);
//開始錄製
recorder_VirtualDisplay();
}
private void recorder_VirtualDisplay() {
//根據根據MediaCodec,dequeueOutputBuffer() 傳回整數值
while (!mQuit.get()) {
int index = mMediaCodec.dequeueOutputBuffer(mBufferInfo, TIMEOUT_US);
//INFO_OUTPUT_FORMAT_CHANGED指後續輸出格式產生變化,譬如放音樂,暫停音樂,
// 呼叫resetOutputFormat()會同時啟動音頻audio_encoder Thread解碼
// 若audio_encoder thread已不在會重新建立,譬如暫停音樂,音源解完,audio_encoder會等於null //若再啟動音樂,又會重新建立新的audio_encoder Thread
if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { resetOutputFormat();//重設格式,啟動MediaMuxer
} else
//INFO_TRY_AGAIN_LATER指請求超時,等待一下
if (index == MediaCodec.INFO_TRY_AGAIN_LATER) {
try {
Thread.sleep(time_out_sleep);
Log.d("TAG","time delay");
} catch (InterruptedException e) {
e.printStackTrace();
}
} else
//大於0有效輸出,開始解碼
if (index >= 0) {
if (!MUXER_START) {
throw new IllegalStateException("MediaMuxer do not call addTrack(format)");
}
encodecToVideoTrack(index);
mMediaCodec.releaseOutputBuffer(index, false);//釋放輸出buffer }
}
release();//最後一定要釋放掉,否則格式會錯
}
//影像解碼器
private void encodecToVideoTrack(int index) {
………………
}
//重設格式
private void resetOutputFormat() {
//產生Thread音源解碼器,處理音源,並啟動,第一次是null會被建立
if(maudio_encoder==null){
maudio_encoder=new audio_encoder(TIMEOUT_US,time_out_sleep,this);
maudio_encoder.start();
}
//若Thread存在,resetOutputFormat
if(maudio_encoder!=null){
maudio_encoder.resetOutputFormat();
}
//vedio resetOutputFormat
MediaFormat newFormat = mMediaCodec.getOutputFormat();
//MediaMuxer add Track
mVideoTrackIndex = mMediaMuxer.addTrack(newFormat);
//啟動MediaMuxer
mMediaMuxer.start();
……………
}
private void release(){
……………………
}
@Override
public void run()
{
startRecord();//開始錄屏
}
public void startRecord() {
preparerecorder();
//設置MediaCodec video
//preapareaudioencoder();
//設置MediaCodec audio
vp = mp.createVirtualDisplay("record_screen", mwindowWidth, mwindowHeight, mscreendi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mSurface, null, null);
//開始錄製
recorder_VirtualDisplay();
}
private void recorder_VirtualDisplay() {
//根據根據MediaCodec,dequeueOutputBuffer() 傳回整數值
while (!mQuit.get()) {
int index = mMediaCodec.dequeueOutputBuffer(mBufferInfo, TIMEOUT_US);
//INFO_OUTPUT_FORMAT_CHANGED指後續輸出格式產生變化,譬如放音樂,暫停音樂,
// 呼叫resetOutputFormat()會同時啟動音頻audio_encoder Thread解碼
// 若audio_encoder thread已不在會重新建立,譬如暫停音樂,音源解完,audio_encoder會等於null //若再啟動音樂,又會重新建立新的audio_encoder Thread
if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { resetOutputFormat();//重設格式,啟動MediaMuxer
} else
//INFO_TRY_AGAIN_LATER指請求超時,等待一下
if (index == MediaCodec.INFO_TRY_AGAIN_LATER) {
try {
Thread.sleep(time_out_sleep);
Log.d("TAG","time delay");
} catch (InterruptedException e) {
e.printStackTrace();
}
} else
//大於0有效輸出,開始解碼
if (index >= 0) {
if (!MUXER_START) {
throw new IllegalStateException("MediaMuxer do not call addTrack(format)");
}
encodecToVideoTrack(index);
mMediaCodec.releaseOutputBuffer(index, false);//釋放輸出buffer }
}
release();//最後一定要釋放掉,否則格式會錯
}
//影像解碼器
private void encodecToVideoTrack(int index) {
………………
}
//重設格式
private void resetOutputFormat() {
//產生Thread音源解碼器,處理音源,並啟動,第一次是null會被建立
if(maudio_encoder==null){
maudio_encoder=new audio_encoder(TIMEOUT_US,time_out_sleep,this);
maudio_encoder.start();
}
//若Thread存在,resetOutputFormat
if(maudio_encoder!=null){
maudio_encoder.resetOutputFormat();
}
//vedio resetOutputFormat
MediaFormat newFormat = mMediaCodec.getOutputFormat();
//MediaMuxer add Track
mVideoTrackIndex = mMediaMuxer.addTrack(newFormat);
//啟動MediaMuxer
mMediaMuxer.start();
……………
}
private void release(){
……………………
}
沒有留言:
張貼留言