2013-07-05 73 views
2

我編寫了一個算法來從AMR文件中提取每個幀。我認爲文件的前6個字節是標題,以下信息是音頻幀。每個音頻幀由幀頭和音頻數據組成。幀頭以字節爲單位指示幀的大小(使用CMR模式​​表 - http://www.developer.nokia.com/Community/Wiki/AMR_format)。幀大小存儲在幀的第一個字節中 - >第二位至第5位,將MSB計爲第一位。從AMR-NB文件中提取音頻幀

算法不起作用,我決定以二進制方式(0和1)在屏幕上顯示每個字節,並且看起來有時幀大小數大於7,並且CMR表只有0 ... 7個值。

下面是CMR表:

CMR  MODE  FRAME SIZE(in bytes) 
0 AMR 4.75  13 
1 AMR 5.15  14 
2 AMR 5.9   16 
3 AMR 6.7   18 
4 AMR 7.4   20 
5 AMR 7.95  21 
6 AMR 10.2  27 
7 AMR 12.2  32 

和我的輸出(來自AMR文件中的每個字節)爲:

0 -> 0 0 0 0 0 0 0 0 
1 -> 0 0 0 0 0 0 0 0 
2 -> 0 0 0 0 0 0 0 0 
3 -> 0 0 0 1 1 0 0 0 
4 -> 0 1 1 0 0 1 1 0 
5 -> 0 0 1 0 1 1 1 0 
6 -> 1 0 0 1 1 1 1 0 
7 -> 0 0 0 0 1 1 1 0 
8 -> 1 1 0 0 1 1 0 0 
9 -> 1 1 1 0 0 1 1 0 
10 -> 0 0 0 0 1 1 1 0 
11 -> 0 0 1 0 1 1 0 0 
12 -> 0 0 0 0 0 0 0 0 
13 -> 0 0 0 0 0 0 0 0 
14 -> 0 0 0 0 0 0 0 0 
15 -> 0 0 0 0 0 0 0 0 
16 -> 1 0 0 1 0 1 1 0 
17 -> 1 1 0 0 1 1 1 0 
18 -> 1 1 1 1 0 1 1 0 
19 -> 1 0 1 1 0 1 1 0 
20 -> 1 1 0 0 1 1 0 0 
21 -> 1 1 1 0 0 1 1 0 
22 -> 0 0 0 0 1 1 1 0 
23 -> 0 0 1 0 1 1 0 0 
24 -> 0 0 0 0 0 0 0 0 
25 -> 0 0 0 0 0 0 0 0 
26 -> 0 1 0 0 0 0 0 0 
27 -> 1 0 0 1 1 0 0 0 
28 -> 1 0 1 1 0 1 1 0 
29 -> 1 1 1 1 0 1 1 0 
30 -> 1 1 1 1 0 1 1 0 
31 -> 0 1 1 0 1 1 1 0 
32 -> 0 0 0 0 0 0 0 0 
33 -> 0 0 0 0 0 0 0 0 
34 -> 0 0 0 0 0 0 0 0 
35 -> 0 0 1 1 0 1 1 0 
36 -> 1 0 1 1 0 1 1 0 
37 -> 0 1 1 0 1 1 1 0 
38 -> 0 0 0 1 0 1 1 0 
39 -> 0 0 1 0 0 1 1 0 
40 -> 0 0 0 0 0 0 0 0 

我把字節NR 6:10011110 - > 0011 NR 3, 3的CMR值相當於18.我跳過18個字節,然後到達字節nr。 6 + 18 = 24:00000000 - 0的CMR值是13,我跳過另一個13字節 - > 24 + 13 = 37:01101110 - > 1101 is 13 WHICH ISN'T IN CMR table

我在做什麼錯?我想以二進制方式打印是正確的。下面是用於讀取每幀(不顯示二進制方式)算法:

private void displayNrOfFrames() throws Exception{ 
     FileInputStream fis = null; 

     try { 
      fis = new FileInputStream(mFile); 
      long result = fis.skip(6); 
      if(result != 6){ 
       throw new Exception("Could not skip first 6 bytes(header) of AMR."); 
      } 

      int number = 0; 
      int bit = 0; 
      byte b; 
      BitSet bs; 
      while((b = Integer.valueOf(fis.read()).byteValue()) != -1){  
       bs = Util.fromByte(b);   
       number = 0; 
       //convert bits [1..4] to number 
       for (int i = 1; i <= 4; i++) { 
        bit = bs.get(i)? 1:0; 
        number += bit*Math.pow(2, 4 - i);     
       } 
       System.out.println(number); 
       if(!CMR_MAP.containsKey(number)){ 
        throw new Exception("Could not parse AMR file."); 
       } 
       //skip the number of bytes of this frame. 
       fis.skip(CMR_MAP.get(number)); 

      }  

     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 

[EDIT]

看來,我做錯了從字節位集合的轉換,然後導致算法失敗。在字節nr.6中,它應該表示爲數字121,但由錯誤的nr 158表示。由於它使用相同的轉換,二進制輸出也是錯誤的。我沒有檢查轉換方法(我沒有在這裏發佈)。對不起打擾。

+0

你可以在vim或任何十六進制編輯器中打開你的文件,看看它顯示爲文件頭嗎? – Rajeev

回答

1

我希望對此回覆我也不遲。

首先第一件事情: 來自同一reference 你可以看到,前6個字節(文件標題)應該是0×23,0×21,×41,送出0x4d,0×52,的0x0A。這是一個不變的價值,應該永遠在那裏。如果它不存在,則該文件可能已損壞,不應使用。所以你不應該盲目地跳過前6個字節。

現在,AMR編解碼器支持DTX(不連續傳輸)。 DTX不過是通過聲碼器檢測到靜音時產生較少數據來節省帶寬的一種方式。您的amr解析器應該準備好期待DTX。爲AMR-NB(AMR窄帶或簡稱AMR)DTX使用模式8。所以,你的CMR地圖應包含以下條目

信號通知

8 AMR SID 6(SID是靜默指示符...指示靜默時段開始)

SID後,會有這將是長度(只是標題1個字節實際靜音幀...無數據),所以你應該有條目

15 AMR NO_DATA 1

模式9-11應該丟棄。模式12-14保留供將來使用(通常這些也被丟棄)。所有上述信息都記住了,單通道AMR正在被使用。

在打印您粘貼

6 - > 1 0 0 1 1 1 1 0

這被認爲是在AMR的Toc頭

0 1 2 3 4 5 6 7 
    +-+-+-+-+-+-+-+-+ 
    |F| FT |Q|P|P| 
    +-+-+-+-+-+-+-+-+ 

對於存儲,F位應該是0,但在你的例子中它是1.最後兩位(這是填充位)必須爲零,但在你的例子中,這些不是0.我相信你的例子沒有在這裏講完整的故事。

+0

感謝您的回答。這裏的問題就像你剛纔提到的那樣,我沒有驗證他們真的屬於AMR頭,就盲目地跳過了前6個字節。 amr文件是一個3gp文件。現在我設法從.3gp中提取原始amr,一切正常。那麼你是否建議用8 - > 6個字節和15 - > 1個字節擴展CMR表? –

+0

和9-11應該丟棄?如果我有一個9的值,我不應該跳過任何字節? –

+1

是的,先生......你的邏輯應該包括大小爲6字節的模式8和大小爲1字節的模式15。通常我會跳過從9到14的所有模式,直到現在,在我實際使用AMR 4年的時間裏,我還沒有遇到過具有這些模式的單幀。 – Rajeev