2012-11-15 119 views
8

我目前正在嘗試使用Android作爲Skype端點。在這個階段,我需要將視頻編碼爲H.264(因爲它是Skype支持的唯一格式)並使用RTP進行封裝,以使流媒體工作。使用MediaCodec H264流

顯然MediaRecorder由於各種原因並不適合這種情況。一種是因爲它在完成後添加了MP4或3GP標頭。另一個原因是爲了將延遲降至最低,硬件加速可能派上用場。這就是爲什麼我想利用框架最近的低水平添加,如MediaCodec,MediaExtractor等。

目前,我打算如下工作。相機將其視頻寫入緩衝區。 MediaCodec使用H264編碼視頻並將結果寫入另一個緩衝區。該緩衝區由RTP封裝器讀取,該封裝器將流數據發送到服務器。這是我的第一個問題:這個計劃對你來說聽起來是否可行?

現在我已經陷入了第一步。由於互聯網上有關使用相機的所有文檔都使用了MediaRecorder,所以在編碼之前我找不到將其原始數據存儲到緩衝區的方法。 addCallbackBuffer適合這個嗎?任何人都有一個例子的鏈接?

接下來,我找不到很多關於MediaCodec的文檔(因爲它相當新)。誰有一個堅實的教程?

最後:關於RTP庫的任何建議?

非常感謝!

回答

5

我對MediaCodec或MediaExtractor還是一無所知,但我對MediaRecorder非常熟悉,併成功實現了基於SpyDroid的RTSP服務器,它捕獲MediaRecorder的H264/AMRNB輸出。基本思想是代碼創建一個本地套接字對,並使用MediaRecorder的setOutputFile將輸出寫入一對套接字中的一個。然後,程序從另一個套接字讀取視頻或音頻流,將其解析爲數據包,然後將每個數據包打包成一個或多個通過UDP發送的RTP數據包。

MediaRecorder確實在完成後添加了MOOV標頭,但如果您使用RTP格式的H264視頻,這並不是問題。基本上,在視頻流開始處有一個「mdat」標題。它有4個字節的頭部長度,接着是4個字節「mdat」。閱讀長度以確定標題的長度,確認它是mdat標題,然後跳過標題數據的其餘部分。從那裏開始,你會得到一串NAL單元,它以單元長度的4個字節開始。小的NAL單元可以用一個RTP包發送,而較大的單元可以分解成FU包。對於RTSP,您還需要提供描述流的SDP標頭。 SpyDroid通過將非常短的電影寫入文件來計算SDP頭中的信息,然後讀取該文件以從結尾提取MOOV頭。我的應用程序總是使用相同的大小,格式和比特率,所以我只是起到一個靜態的字符串:

public static final String SDP_STRING = 
     "m=video 5006 RTP/AVP 96\n" 
       + "b=RR:0\n" 
       + "a=rtpmap:96 H264/90000\n" 
       + "a=fmtp:96 packetization-mode=1;profile-level-id=428028;sprop-parameter-sets=Z0KAKJWgKA9E,aM48gA==;\n" 
       + "a=control:trackID=0\n" 
       + "m=audio 5004 RTP/AVP 96\n" 
       + "b=AS:128\n" 
       + "b=RR:0\n" 
       + "a=rtpmap:96 AMR/8000\n" 
       + "a=fmtp:96 octet-align=1;\n" 
       + "a=control:trackID=1\n"; 

這是我的640x480x10fps頭,H264視頻,與8000/16/1 AMRNB音頻。

我可以提醒你的一件事:如果你使用MediaRecorder,你的預覽回調將永遠不會被調用。這隻適用於相機模式,而不是在錄製視頻時使用。在視頻錄製過程中,我無法找到以非壓縮格式訪問預覽圖像的任何方式。

我強烈建議您查看SpyDroid的代碼。這需要一些挖掘,但我敢打賭,你想要的是在那裏。

+0

我知道這個評論很晚(不知道這是否允許),但現在3年過去了,這仍然是相關的,或者現在有更好的方法來做到這一點嗎? –

0

你的計劃是明確可行的。您可以註冊一個Camera.PreviewCallback,它將圖片數據放入MediaCodec中。您讀取輸出並將其作爲RTP發送。一般而言,這很容易,但在不同的設備上存在各種缺陷,如未記錄的色彩空間和不同的MediaCodec行爲,但這種缺陷是可能的。

8

UPDATE
我終於能夠從h264幀創建適當的RTP包。這是你必須記住的(其實很簡單):

編碼器確實爲每個幀創建NAL頭。但是它將每幀返回爲h264 字節流。這意味着每個幀以三個0字節和一個1字節開始。您只需刪除這些起始前綴,然後將幀放入RTP包(或使用FU-As進行拆分)。

我們您的問題:

我不能找到一種方法,它的原始數據保存到緩衝區在編碼之前。 addCallbackBuffer是否適合這個?

您應該使用camera.setPreviewCallback(...),並將每個幀添加到編碼器。

我找不到很多關於MediaCodec的文檔(因爲它是相當新的)。誰有一個堅實的教程?

這應該是關於MediaCodec如何工作的一個很好的介紹。 http://dpsm.wordpress.com/2012/07/28/android-mediacodec-decoded/

最後:關於RTP庫的任何建議?

我正在使用jlibrtp來完成這項工作。