在java类和android活动之间进行流式传输时,音频不清晰



我有一个android活动,它连接到一个java类,并以套接字的形式向它发送数据包。该类接收声音包并将其发送到电脑扬声器。代码运行良好,但在电脑扬声器中播放声音时会出现持续的抖动/中断。

android活动:

public class SendActivity extends Activity {
    private Button startButton, stopButton;
    public byte[] buffer;
    public static DatagramSocket socket;
    private int port = 50005;
    AudioRecord recorder;
    private int sampleRate = 8000;
    @SuppressWarnings("deprecation")
    private int channelConfig = AudioFormat.CHANNEL_IN_MONO;
    private int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
    int minBufSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig,
            audioFormat);
    private boolean status = true;

    int bufferSizeInBytes;
    int bufferSizeInShorts;
      int shortsRead;
      short audioBuffer[];
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_send);
        startButton = (Button) findViewById(R.id.start_button);
        stopButton = (Button) findViewById(R.id.stop_button);
        startButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                status = true;
                startStreaming();

            }
        });
        stopButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                status = false;
                recorder.release();
                Log.d("VS", "Recorder released");
            }
        });
        minBufSize += 5120;
        System.out.println("minBufSize: " + minBufSize);
    }

    public void startStreaming() {
        Thread streamThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    DatagramSocket socket = new DatagramSocket();
                    Log.d("VS", "Socket Created");
                    byte[] buffer = new byte[minBufSize];
                    Log.d("VS", "Buffer created of size " + minBufSize);
                    DatagramPacket packet;
//machine's IP
                    final InetAddress destination = InetAddress
                            .getByName("192.168.1.20");
                    Log.d("VS", "Address retrieved");
                    recorder = new AudioRecord(MediaRecorder.AudioSource.VOICE_RECOGNITION,
                            sampleRate, channelConfig, audioFormat,
                            minBufSize * 10);
                    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);
                        System.out.println("MinBufferSize: " + minBufSize);
                    }
                } catch (UnknownHostException e) {
                    Log.e("VS", "UnknownHostException");
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.e("VS", "IOException");
                }
            }
        });
        streamThread.start();
    }

}

android布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".SendActivity" >

    <Button
        android:id="@+id/stop_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/start_button"
        android:layout_alignBottom="@+id/start_button"
        android:layout_toRightOf="@+id/start_button"
        android:text="Stop" />
    <Button
        android:id="@+id/start_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="79dp"
        android:layout_marginTop="163dp"
        android:text="Start" />
</RelativeLayout>

Android清单:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.audiostreamsample"
    android:versionCode="1"
    android:versionName="1.0" >
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" >
    </uses-permission>
    <uses-permission android:name="android.permission.INTERNET" >
    </uses-permission>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" >
    </uses-permission>
    <uses-permission android:name="android.permission.READ_PHONE_STATE" >
    </uses-permission>
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.CALL_PHONE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.audiostreamsample.SendActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

接收数据包并将其扔到电脑扬声器的类:

class Server {
AudioInputStream audioInputStream;
static AudioInputStream ais;
static AudioFormat format;
static boolean status = true;
static int port = 50005;
static int sampleRate = 8000;

public static void main(String args[]) throws Exception {

    DatagramSocket serverSocket = new DatagramSocket(50005);
    /**
     * Formula for lag = (byte_size/sample_rate)*2
     * Byte size 9728 will produce ~ 0.45 seconds of lag. Voice slightly broken.
     * Byte size 1400 will produce ~ 0.06 seconds of lag. Voice extremely broken.
     * Byte size 4000 will produce ~ 0.18 seconds of lag. Voice slightly more broken then 9728.
     */
    byte[] receiveData = new byte[5000];
    format = new AudioFormat(sampleRate, 16, 1, true, false);
    while (status == true) {
        DatagramPacket receivePacket = new DatagramPacket(receiveData,
                receiveData.length);
        serverSocket.receive(receivePacket);
        ByteArrayInputStream baiss = new ByteArrayInputStream(
                receivePacket.getData());
        ais = new AudioInputStream(baiss, format, receivePacket.getLength());
        toSpeaker(receivePacket.getData());
    }

}
public static void toSpeaker(byte soundbytes[]) {
    try {
        DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, format);
        SourceDataLine sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);
        sourceDataLine.open(format);
        FloatControl volumeControl = (FloatControl) sourceDataLine.getControl(FloatControl.Type.MASTER_GAIN);
        volumeControl.setValue(6.0206f);
        sourceDataLine.start();
        sourceDataLine.open(format);
        sourceDataLine.start();
        System.out.println("format? :" + sourceDataLine.getFormat());
        sourceDataLine.write(soundbytes, 0, soundbytes.length);
        System.out.println(soundbytes.toString());
        sourceDataLine.drain();
        sourceDataLine.close();
    } catch (Exception e) {
        System.out.println("Not working in speakers...");
        e.printStackTrace();
    }
}
}

如果你想在IDE中测试应用程序,那么只需创建两个不同的项目,一个用于android应用程序,另一个用于服务器类。

在android应用程序中,只需添加您机器的IP并在设备上运行该应用程序,手机和计算机就应该属于同一网络。请将服务器类作为java应用程序执行。

抖动会很突出,令人恼火,但声音会或多或少地清晰。请建议我如何获得更清晰的输出。

您需要对实际流媒体提供一些编码支持。除了发送数据报并抱着最好的希望,还有更多的事情需要考虑。

真正的人际网络并不完美。

  • 延迟:数据包需要时间
  • 抖动:数据包在飞行中所花费的时间不是恒定的
  • 丢弃的包裹:有时他们做不到
  • 重新排序:有时数据包的到达顺序与发送顺序不同

你应该阅读像RTP这样的简单媒体流协议,也许可以使用一个向两端提供RTP的库。RTP通常位于UDP之上。

音频的TCP流传输可能不如UDP/RTP有用,因为你必须关闭Nagling。

您至少需要在接收器端有一个小缓冲区,以防止缓冲区空导致声音丢失。

最新更新