2012-02-11 90 views
29

我正嘗試通過WiFi將音頻從麥克風從1 Android傳輸到另一個Android。 在看了一些例子之後,我在每個應用中做了一個單獨的活動,1個捕獲併發送音頻,另一個接收。通過WiFi在Android手機之間傳輸語音

我已經使用Audiorecord和Audiotrack類來捕捉和播放。然而,我只是聽到一些crack啪聲(現在停止後,我做了一些改變,雖然我恢復了回來)

活動發送語音。

public class VoiceSenderActivity extends Activity { 

private EditText target; 
private TextView streamingLabel; 
private Button startButton,stopButton; 

public byte[] buffer; 
public static DatagramSocket socket; 
private int port=50005;   //which port?? 
AudioRecord recorder; 

//Audio Configuration. 
private int sampleRate = 8000;  //How much will be ideal? 
private int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;  
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;  

private boolean status = true; 




@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main); 

    target = (EditText) findViewById (R.id.target_IP); 
    streamingLabel = (TextView) findViewById(R.id.streaming_label); 
    startButton = (Button) findViewById (R.id.start_button); 
    stopButton = (Button) findViewById (R.id.stop_button); 

    streamingLabel.setText("Press Start! to begin"); 

    startButton.setOnClickListener (startListener); 
    stopButton.setOnClickListener (stopListener); 
} 

private final OnClickListener stopListener = new OnClickListener() { 

    @Override 
    public void onClick(View arg0) { 
       status = false; 
       recorder.release(); 
       Log.d("VS","Recorder released"); 
    } 

}; 

private final OnClickListener startListener = new OnClickListener() { 

    @Override 
    public void onClick(View arg0) { 
       status = true; 
       startStreaming();   
    } 

}; 

public void startStreaming() { 


    Thread streamThread = new Thread(new Runnable() { 

     @Override 
     public void run() { 
      try { 


       int minBufSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat); 
       DatagramSocket socket = new DatagramSocket(); 
       Log.d("VS", "Socket Created"); 

       byte[] buffer = new byte[minBufSize]; 

       Log.d("VS","Buffer created of size " + minBufSize); 
       DatagramPacket packet; 

       final InetAddress destination = InetAddress.getByName(target.getText().toString()); 
       Log.d("VS", "Address retrieved"); 


       recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,sampleRate,channelConfig,audioFormat,minBufSize); 
       Log.d("VS", "Recorder initialized"); 

       recorder.startRecording(); 


       while(status == true) { 


        //reading data from MIC into buffer 
        minBufSize = recorder.read(buffer, 0, buffer.length); 

        //putting buffer in the packet 
        packet = new DatagramPacket (buffer,buffer.length,destination,port); 

        socket.send(packet); 


       } 



      } catch(UnknownHostException e) { 
       Log.e("VS", "UnknownHostException"); 
      } catch (IOException e) { 
       Log.e("VS", "IOException"); 
      } 


     } 

    }); 
    streamThread.start(); 
} 
} 

活動接收語音

public class VoiceReceiverActivity extends Activity { 


private Button receiveButton,stopButton; 

public static DatagramSocket socket; 
private AudioTrack speaker; 

//Audio Configuration. 
private int sampleRate = 8000;  //How much will be ideal? 
private int channelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;  
private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;  

private boolean status = true; 


@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main); 

    receiveButton = (Button) findViewById (R.id.receive_button); 
    stopButton = (Button) findViewById (R.id.stop_button); 
    findViewById(R.id.receive_label); 

    receiveButton.setOnClickListener(receiveListener); 
    stopButton.setOnClickListener(stopListener); 

} 


private final OnClickListener stopListener = new OnClickListener() { 

    @Override 
    public void onClick(View v) { 
     status = false; 
     speaker.release(); 
     Log.d("VR","Speaker released"); 

    } 

}; 


private final OnClickListener receiveListener = new OnClickListener() { 

    @Override 
    public void onClick(View arg0) { 
     status = true; 
     startReceiving(); 

    } 

}; 

public void startReceiving() { 

    Thread receiveThread = new Thread (new Runnable() { 

     @Override 
     public void run() { 

      try { 

       DatagramSocket socket = new DatagramSocket(50005); 
       Log.d("VR", "Socket Created"); 


       byte[] buffer = new byte[256]; 


       //minimum buffer size. need to be careful. might cause problems. try setting manually if any problems faced 
       int minBufSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat); 

       speaker = new AudioTrack(AudioManager.STREAM_MUSIC,sampleRate,channelConfig,audioFormat,minBufSize,AudioTrack.MODE_STREAM); 

       speaker.play(); 

       while(status == true) { 
        try { 


         DatagramPacket packet = new DatagramPacket(buffer,buffer.length); 
         socket.receive(packet); 
         Log.d("VR", "Packet Received"); 

         //reading content from packet 
         buffer=packet.getData(); 
         Log.d("VR", "Packet data read into buffer"); 

         //sending data to the Audiotrack obj i.e. speaker 
         speaker.write(buffer, 0, minBufSize); 
         Log.d("VR", "Writing buffer content to speaker"); 

        } catch(IOException e) { 
         Log.e("VR","IOException"); 
        } 
       } 


      } catch (SocketException e) { 
       Log.e("VR", "SocketException"); 
      } 


     } 

    }); 
    receiveThread.start(); 
} 

} 

我使用Wireshark來檢查是否正在發送的數據包,我可以看到的數據包。但是,源是發送設備的MAC地址,目標也是物理地址。不知道這是否相關。

那麼問題是什麼?

+1

至少有三個問題需要處理:數據延遲(或丟失),總體數據吞吐量以及稍微不匹配採樣頻率的可能性。實際的IP電話必須具備處理這三者的手段。不匹配的時鐘是非常棘手的 - 最初你可以引入一個延遲來給出一些緩衝允許,但是如果發送者速度較慢,你將用盡緩衝區,並且接收器將會缺乏數據;而如果發送者速度更快,則緩衝區最終會隨未播放數據溢出。 – 2012-12-01 17:32:50

+0

我確實設法實現了這一點。沒有真正存在不匹配頻率的問題。數據延遲,是的。有一種我自己的協議來匹配接收器/發送器時鐘。最後,它確實工作,但只有一些滯後(隨着距離無線路由器的增加而增加) – Alabhya 2012-12-02 18:37:41

+0

嘿,那裏,我爲上面的代碼實現了一個測試應用程序,在下面提出了所有必要的修改,但我仍然有問題。兩個手機之間的通訊沒有問題,但我認爲麥克風不能正常錄音,因爲我聽不到另一端的聲音。您是否可以鏈接到我可以看一下的示例解決方案? – chuckliddell0 2013-03-11 14:48:16

回答

3

我會盡量把問題分成三部分。

第1部分

確保Socket連接工作正常 通過評論與音頻

第2部分

發送單純只是任意文本一切消息[地獄o WiFi],然後在接收端應用程序中接收並打印它。

第3部分

無論是記錄實際工作? 嘗試在單獨的項目中測試您的錄製方式,以查看它是否正常工作。使用this代碼來捕捉麥克風並播放它。

我的經驗

我曾經在一個類似的項目,並對其進行測試,我所做的就是記錄後,我寫了錄製的音頻數據在SD卡

文件(它會是原始音頻,所以大多數音樂播放器將無法播放它... mPlayer應該播放它我猜)

+0

好吧,我得到它的工作。聲音分散得太厲害,而且有滯後。需要爲此找出正確的採樣率和緩衝區大小。如果有任何投入就會很好。 無論如何,非常感謝。你說的幫助。 – Alabhya 2012-02-17 19:40:50

+0

在您的接收器活動中,在startReceiving()方法中,不要使用256作爲緩衝區大小,而是使用您在下一行中獲取的minBufSize。除此之外,可能想用不同的採樣率玩一下,但即使是8k也應該是不錯的。 – 2012-02-18 05:54:45

+1

好吧,我得到它的工作。顯然minBufSize太多了,因此滯後和打破。將minBufSize設置爲256,初始化AudioRecord和AudioTrack對象的同時將緩衝區大小設置爲minBufSize * 10 ..嘗試了不同的採樣率組合並獲得了滿意的結果。 非常感謝! – Alabhya 2012-02-18 10:34:48

2

您需要認真考慮您使用UDP(DatagramSocket類)作爲您的網絡協議。

UDP是一個輕量級協議,不保證保持接收數據包的順序。這可能是音頻混亂的原因之一。無序接收到的數據包將導致數據包的音頻亂序播放。在這些亂序數據包的邊界處,您會聽到音頻樣本被有效破壞的點擊/彈出。除此之外,UDP數據包不保證成功傳送。任何丟棄的數據包顯然會增加任何聽到的歪曲或歪曲。

對於最佳音頻質量,TCP(套接字類)將是更好的選擇。 TCP是一個更強大的協議,它將維護數據包的接收順序。它還具有內置錯誤檢查功能,並將重新發送任何丟棄的數據包。但是,由於這種注意功能,TCP具有較高的網絡開銷。

我開始這個迴應,說你需要仔細考慮你使用的協議。這是因爲有一種情況可以使用,取決於你對什麼很重要。

如果你想要超低延遲播放,但很樂意犧牲音頻質量,那麼UDP將工作。但是,需要一些實驗才能找到最佳的緩衝區和樣本大小。

如果您希望儘可能獲得儘可能最好的音頻重新制作而不失真,但很樂意介紹稍微更多的延遲,那麼TCP就是要走的路線。

我不能說TCP會增加多少延遲。但有可能在不影響用戶體驗的情況下實施。找出答案的唯一方法就是試試看。

相關問題