私は実装しようとしています
AudioRecord (MIC) ->
PCM -> AAC Encoder
AAC -> PCM Decode
-> AudioTrack?? (SPEAKER)
MediaCodec
on Android 4.1+(API16)。
まず、正常に実装されましたが(正しく最適化されているとは限りません)PCM -> AAC Encoder
by MediaCodec
by下記のように
private boolean setEncoder(int rate)
{
encoder = MediaCodec.createEncoderByType("audio/mp4a-latm");
MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);
format.setInteger(MediaFormat.KEY_BIT_RATE, 64 * 1024);//AAC-HE 64kbps
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectHE);
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
return true;
}
入力:PCMビットレート= 44100(Hz)x 16(bit)x 1(Monoral) = 705600 bit/s
出力:AAC-HEビットレート= 64 x 1024(ビット)= 65536ビット/秒
したがって、データサイズはほぼ圧縮されますx11
、およびログを観察することでこの動作を確認しました
データサイズはほぼ圧縮されていますx11
、 ここまでは順調ですね。
これで、エンコードされたデータを受信してデコードするUDPサーバーができました。
デコーダープロファイルは次のように設定されます。
private boolean setDecoder(int rate)
{
decoder = MediaCodec.createDecoderByType("audio/mp4a-latm");
MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);
format.setInteger(MediaFormat.KEY_BIT_RATE, 64 * 1024);//AAC-HE 64kbps
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectHE);
decoder.configure(format, null, null, 0);
return true;
}
UDPserverパケットバッファサイズは1024
これは圧縮されたAACデータであるため、デコードサイズは
約1024 x11
、しかし実際の結果は
おおよそx8
、そして私は何か間違っていると感じています。
デコーダコードは次のとおりです。
IOudpPlayer = new Thread(new Runnable()
{
public void run()
{
SocketAddress sockAddress;
String address;
int len = 1024;
byte[] buffer2 = new byte[len];
DatagramPacket packet;
byte[] data;
ByteBuffer[] inputBuffers;
ByteBuffer[] outputBuffers;
ByteBuffer inputBuffer;
ByteBuffer outputBuffer;
MediaCodec.BufferInfo bufferInfo;
int inputBufferIndex;
int outputBufferIndex;
byte[] outData;
try
{
decoder.start();
isPlaying = true;
while (isPlaying)
{
try
{
packet = new DatagramPacket(buffer2, len);
ds.receive(packet);
sockAddress = packet.getSocketAddress();
address = sockAddress.toString();
Log.d("UDP Receiver"," received !!! from " + address);
data = new byte[packet.getLength()];
System.arraycopy(packet.getData(), packet.getOffset(), data, 0, packet.getLength());
Log.d("UDP Receiver", data.length + " bytes received");
//===========
inputBuffers = decoder.getInputBuffers();
outputBuffers = decoder.getOutputBuffers();
inputBufferIndex = decoder.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0)
{
inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(data);
decoder.queueInputBuffer(inputBufferIndex, 0, data.length, 0, 0);
}
bufferInfo = new MediaCodec.BufferInfo();
outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, 0);
while (outputBufferIndex >= 0)
{
outputBuffer = outputBuffers[outputBufferIndex];
outputBuffer.position(bufferInfo.offset);
outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
outData = new byte[bufferInfo.size];
outputBuffer.get(outData);
Log.d("AudioDecoder", outData.length + " bytes decoded");
decoder.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, 0);
}
//===========
}
catch (IOException e)
{
}
}
decoder.stop();
}
catch (Exception e)
{
}
}
});
完全なコード:
https://Gist.github.com/kenokabe/9029256
許可も必要です:
<uses-permission Android:name="Android.permission.INTERNET"></uses-permission>
<uses-permission Android:name="Android.permission.RECORD_AUDIO"></uses-permission>
メンバー fadden Googleで働いている人が私に言った
出力バッファの位置と制限を設定していないようです。
VP8 Encoding Nexus 5はempty/0-Framesを返します を読みましたが、正しく実装する方法がわかりません。
UPDATE:どこで変更するかを理解しました
出力バッファの位置と制限を設定していないようです。
、したがって、エンコーダーとデコーダーのwhileループ内に次のように2行追加します:
outputBuffer.position(bufferInfo.offset);
outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
https://Gist.github.com/kenokabe/9029256/revisions
ただし、結果は同じです。
そして今、私は思う、エラー:W/SoftAAC2﹕ AAC decoder returned error 16388, substituting silence.
は、このデコーダーが最初から完全に失敗することを示します。再びthe data is not seekable
問題。 AndroidでAACストリームをシークする AACデコーダーがこの方法でストリーミングデータを処理できず、ヘッダーを追加するだけでは非常に残念です。
UPDATE2:UDPレシーバーが間違っていたため、変更されました
https://Gist.github.com/kenokabe/9029256
さて、エラー
W/SoftAAC2﹕ AAC decoder returned error 16388, substituting silence.
消えました!!
そのため、少なくともエラーなしでデコーダが機能することを示しています。
ただし、これは1サイクルのログです。
D/AudioRecoder﹕ 4096 bytes read
D/AudioEncoder﹕ 360 bytes encoded
D/UDP Receiver﹕ received !!! from /127.0.0.1:39000
D/UDP Receiver﹕ 360 bytes received
D/AudioDecoder﹕ 8192 bytes decoded
PCM(4096)-> AACencoded(360)-> UDP-AAC(360)->(想定される)PCM(8192)
最終的な結果は、元のPCMの約2倍のサイズですが、まだ問題があります。
ここで私の質問は
サンプルコードを適切に最適化して正しく動作することはできますか?
AudioTrack
APIを使用して、デコードされたPCM生データをオンザフライで再生するのは正しい方法ですか。適切な方法を教えてください。サンプルコードを歓迎します。
ありがとうございました。
PS。私のプロジェクトはAndroid4.1 +(API16)をターゲットにしていますが、API18(Andeoid 4.3+)の方が読みやすいですが、残念なことに互換性の理由から、ここではMediaMuxerなどをスキップする必要があります...
テスト後、これはあなたのコードを修正することから思いついたものです:
package com.example.app;
import Android.app.Activity;
import Android.media.AudioManager;
import Android.media.MediaCodecInfo;
import Android.media.MediaFormat;
import Android.os.Bundle;
import Android.media.AudioFormat;
import Android.media.AudioRecord;
import Android.media.AudioTrack;
import Android.media.MediaCodec;
import Android.media.MediaRecorder.AudioSource;
import Android.util.Log;
import Java.io.IOException;
import Java.net.DatagramPacket;
import Java.net.DatagramSocket;
import Java.net.InetAddress;
import Java.net.SocketAddress;
import Java.net.SocketException;
import Java.nio.ByteBuffer;
public class MainActivity extends Activity
{
private AudioRecord recorder;
private AudioTrack player;
private MediaCodec encoder;
private MediaCodec decoder;
private short audioFormat = AudioFormat.ENCODING_PCM_16BIT;
private short channelConfig = AudioFormat.CHANNEL_IN_MONO;
private int bufferSize;
private boolean isRecording;
private boolean isPlaying;
private Thread IOrecorder;
private Thread IOudpPlayer;
private DatagramSocket ds;
private final int localPort = 39000;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IOrecorder = new Thread(new Runnable()
{
public void run()
{
int read;
byte[] buffer1 = new byte[bufferSize];
ByteBuffer[] inputBuffers;
ByteBuffer[] outputBuffers;
ByteBuffer inputBuffer;
ByteBuffer outputBuffer;
MediaCodec.BufferInfo bufferInfo;
int inputBufferIndex;
int outputBufferIndex;
byte[] outData;
DatagramPacket packet;
try
{
encoder.start();
recorder.startRecording();
isRecording = true;
while (isRecording)
{
read = recorder.read(buffer1, 0, bufferSize);
// Log.d("AudioRecoder", read + " bytes read");
//------------------------
inputBuffers = encoder.getInputBuffers();
outputBuffers = encoder.getOutputBuffers();
inputBufferIndex = encoder.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0)
{
inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(buffer1);
encoder.queueInputBuffer(inputBufferIndex, 0, buffer1.length, 0, 0);
}
bufferInfo = new MediaCodec.BufferInfo();
outputBufferIndex = encoder.dequeueOutputBuffer(bufferInfo, 0);
while (outputBufferIndex >= 0)
{
outputBuffer = outputBuffers[outputBufferIndex];
outputBuffer.position(bufferInfo.offset);
outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
outData = new byte[bufferInfo.size];
outputBuffer.get(outData);
// Log.d("AudioEncoder ", outData.length + " bytes encoded");
//-------------
packet = new DatagramPacket(outData, outData.length,
InetAddress.getByName("127.0.0.1"), localPort);
ds.send(packet);
//------------
encoder.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = encoder.dequeueOutputBuffer(bufferInfo, 0);
}
// ----------------------;
}
encoder.stop();
recorder.stop();
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
IOudpPlayer = new Thread(new Runnable()
{
public void run()
{
SocketAddress sockAddress;
String address;
int len = 2048
byte[] buffer2 = new byte[len];
DatagramPacket packet;
byte[] data;
ByteBuffer[] inputBuffers;
ByteBuffer[] outputBuffers;
ByteBuffer inputBuffer;
ByteBuffer outputBuffer;
MediaCodec.BufferInfo bufferInfo;
int inputBufferIndex;
int outputBufferIndex;
byte[] outData;
try
{
player.play();
decoder.start();
isPlaying = true;
while (isPlaying)
{
try
{
packet = new DatagramPacket(buffer2, len);
ds.receive(packet);
sockAddress = packet.getSocketAddress();
address = sockAddress.toString();
// Log.d("UDP Receiver"," received !!! from " + address);
data = new byte[packet.getLength()];
System.arraycopy(packet.getData(), packet.getOffset(), data, 0, packet.getLength());
// Log.d("UDP Receiver", data.length + " bytes received");
//===========
inputBuffers = decoder.getInputBuffers();
outputBuffers = decoder.getOutputBuffers();
inputBufferIndex = decoder.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0)
{
inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(data);
decoder.queueInputBuffer(inputBufferIndex, 0, data.length, 0, 0);
}
bufferInfo = new MediaCodec.BufferInfo();
outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, 0);
while (outputBufferIndex >= 0)
{
outputBuffer = outputBuffers[outputBufferIndex];
outputBuffer.position(bufferInfo.offset);
outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
outData = new byte[bufferInfo.size];
outputBuffer.get(outData);
// Log.d("AudioDecoder", outData.length + " bytes decoded");
player.write(outData, 0, outData.length);
decoder.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, 0..
自己回答、これまでの最善の努力
package com.example.app;
import Android.app.Activity;
import Android.media.AudioManager;
import Android.media.MediaCodecInfo;
import Android.media.MediaFormat;
import Android.os.Bundle;
import Android.media.AudioFormat;
import Android.media.AudioRecord;
import Android.media.AudioTrack;
import Android.media.MediaCodec;
import Android.media.MediaRecorder.AudioSource;
import Android.util.Log;
import Java.io.IOException;
import Java.net.DatagramPacket;
import Java.net.DatagramSocket;
import Java.net.InetAddress;
import Java.net.SocketAddress;
import Java.net.SocketException;
import Java.nio.ByteBuffer;
public class MainActivity extends Activity
{
private AudioRecord recorder;
private AudioTrack player;
private MediaCodec encoder;
private MediaCodec decoder;
private short audioFormat = AudioFormat.ENCODING_PCM_16BIT;
private short channelConfig = AudioFormat.CHANNEL_IN_MONO;
private int bufferSize;
private boolean isRecording;
private boolean isPlaying;
private Thread IOrecorder;
private Thread IOudpPlayer;
private DatagramSocket ds;
private final int localPort = 39000;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IOrecorder = new Thread(new Runnable()
{
public void run()
{
int read;
byte[] buffer1 = new byte[bufferSize];
ByteBuffer[] inputBuffers;
ByteBuffer[] outputBuffers;
ByteBuffer inputBuffer;
ByteBuffer outputBuffer;
MediaCodec.BufferInfo bufferInfo;
int inputBufferIndex;
int outputBufferIndex;
byte[] outData;
DatagramPacket packet;
try
{
encoder.start();
recorder.startRecording();
isRecording = true;
while (isRecording)
{
read = recorder.read(buffer1, 0, bufferSize);
// Log.d("AudioRecoder", read + " bytes read");
//------------------------
inputBuffers = encoder.getInputBuffers();
outputBuffers = encoder.getOutputBuffers();
inputBufferIndex = encoder.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0)
{
inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(buffer1);
encoder.queueInputBuffer(inputBufferIndex, 0, buffer1.length, 0, 0);
}
bufferInfo = new MediaCodec.BufferInfo();
outputBufferIndex = encoder.dequeueOutputBuffer(bufferInfo, 0);
while (outputBufferIndex >= 0)
{
outputBuffer = outputBuffers[outputBufferIndex];
outputBuffer.position(bufferInfo.offset);
outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
outData = new byte[bufferInfo.size];
outputBuffer.get(outData);
// Log.d("AudioEncoder", outData.length + " bytes encoded");
//-------------
packet = new DatagramPacket(outData, outData.length,
InetAddress.getByName("127.0.0.1"), localPort);
ds.send(packet);
//------------
encoder.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = encoder.dequeueOutputBuffer(bufferInfo, 0);
}
// ----------------------;
}
encoder.stop();
recorder.stop();
}
catch (Exception e)
{
e.printStackTrace();
}
}
});
IOudpPlayer = new Thread(new Runnable()
{
public void run()
{
SocketAddress sockAddress;
String address;
int len = 1024;
byte[] buffer2 = new byte[len];
DatagramPacket packet;
byte[] data;
ByteBuffer[] inputBuffers;
ByteBuffer[] outputBuffers;
ByteBuffer inputBuffer;
ByteBuffer outputBuffer;
MediaCodec.BufferInfo bufferInfo;
int inputBufferIndex;
int outputBufferIndex;
byte[] outData;
try
{
player.play();
decoder.start();
isPlaying = true;
while (isPlaying)
{
try
{
packet = new DatagramPacket(buffer2, len);
ds.receive(packet);
sockAddress = packet.getSocketAddress();
address = sockAddress.toString();
// Log.d("UDP Receiver"," received !!! from " + address);
data = new byte[packet.getLength()];
System.arraycopy(packet.getData(), packet.getOffset(), data, 0, packet.getLength());
// Log.d("UDP Receiver", data.length + " bytes received");
//===========
inputBuffers = decoder.getInputBuffers();
outputBuffers = decoder.getOutputBuffers();
inputBufferIndex = decoder.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0)
{
inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(data);
decoder.queueInputBuffer(inputBufferIndex, 0, data.length, 0, 0);
}
bufferInfo = new MediaCodec.BufferInfo();
outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, 0);
while (outputBufferIndex >= 0)
{
outputBuffer = outputBuffers[outputBufferIndex];
outputBuffer.position(bufferInfo.offset);
outputBuffer.limit(bufferInfo.offset + bufferInfo.size);
outData = new byte[bufferInfo.size];
outputBuffer.get(outData);
// Log.d("AudioDecoder", outData.length + " bytes decoded");
player.write(outData, 0, outData.length);
decoder.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = decoder.dequeueOutputBuffer(bufferInfo, 0);
}
//===========
}
catch (IOException e)
{
}
}
decoder.stop();
player.stop();
}
catch (Exception e)
{
}
}
});
//===========================================================
int rate = findAudioRecord();
if (rate != -1)
{
Log.v("=========media ", "ready: " + rate);
Log.v("=========media channel ", "ready: " + channelConfig);
boolean encoderReady = setEncoder(rate);
Log.v("=========encoder ", "ready: " + encoderReady);
if (encoderReady)
{
boolean decoderReady = setDecoder(rate);
Log.v("=========decoder ", "ready: " + decoderReady);
if (decoderReady)
{
Log.d("=======bufferSize========", "" + bufferSize);
try
{
setPlayer(rate);
ds = new DatagramSocket(localPort);
IOudpPlayer.start();
IOrecorder.start();
}
catch (SocketException e)
{
e.printStackTrace();
}
}
}
}
}
protected void onDestroy()
{
recorder.release();
player.release();
encoder.release();
decoder.release();
}
/*
protected void onResume()
{
// isRecording = true;
}
protected void onPause()
{
isRecording = false;
}
*/
private int findAudioRecord()
{
for (int rate : new int[]{44100})
{
try
{
Log.v("===========Attempting rate ", rate + "Hz, bits: " + audioFormat + ", channel: " + channelConfig);
bufferSize = AudioRecord.getMinBufferSize(rate, channelConfig, audioFormat);
if (bufferSize != AudioRecord.ERROR_BAD_VALUE)
{
// check if we can instantiate and have a success
recorder = new AudioRecord(AudioSource.MIC, rate, channelConfig, audioFormat, bufferSize);
if (recorder.getState() == AudioRecord.STATE_INITIALIZED)
{
Log.v("===========final rate ", rate + "Hz, bits: " + audioFormat + ", channel: " + channelConfig);
return rate;
}
}
}
catch (Exception e)
{
Log.v("error", "" + rate);
}
}
return -1;
}
private boolean setEncoder(int rate)
{
encoder = MediaCodec.createEncoderByType("audio/mp4a-latm");
MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, rate);
format.setInteger(MediaFormat.KEY_BIT_RATE, 64 * 1024);//AAC-HE 64kbps
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectHE);
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
return true;
}
private boolean setDecoder(int rate)
{
decoder = MediaCodec.createDecoderByType("audio/mp4a-latm");
MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, rate);
format.setInteger(MediaFormat.KEY_BIT_RATE, 64 * 1024);//AAC-HE 64kbps
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectHE);
decoder.configure(format, null, null, 0);
return true;
}
private boolean setPlayer(int rate)
{
int bufferSizePlayer = AudioTrack.getMinBufferSize(rate, AudioFormat.CHANNEL_OUT_MONO, audioFormat);
Log.d("====buffer Size player ", String.valueOf(bufferSizePlayer));
player= new AudioTrack(AudioManager.STREAM_MUSIC, rate, AudioFormat.CHANNEL_OUT_MONO, audioFormat, bufferSizePlayer, AudioTrack.MODE_STREAM);
if (player.getState() == AudioTrack.STATE_INITIALIZED)
{
return true;
}
else
{
return false;
}
}
}
上記のコードを試してみましたが、正常に動作しませんでした。デコードされた出力に多くの沈黙が挿入されていました。問題は、デコーダに適切な「csd」値を設定していませんでした。
したがって、ログに「無音」またはエラーをスローするデコーダーが表示される場合は、メディアデコーダー形式に以下を追加してください。
int profile = 2; //AAC LC
int freqIdx = 11; //8KHz
int chanCfg = 1; //Mono
ByteBuffer csd = ByteBuffer.allocate(2);
csd.put(0, (byte) (profile << 3 | freqIdx >> 1));
csd.put(1, (byte)((freqIdx & 0x01) << 7 | chanCfg << 3));
mediaFormat.setByteBuffer("csd-0", csd);
D/AudioRecoder:4096バイトの読み取りD/AudioEncoder:360バイトのエンコードされたD/UDP Receiver:の受信!!! /127.0.0.1:39000 D/UDPレシーバーから360バイト受信D/AudioDecoder:8192バイトデコード
これは、エンコードされたデータがMONOであっても、accデコーダーが常にステレオチャネルにデコードするためです。エンコード側がステレオチャンネルに設定されている場合、次のようになります。
D/AudioRecoder:8192バイト読み取りD/AudioEncoder:360バイトエンコードD/UDP Receiver:受信!!! /127.0.0.1:39000 D/UDPレシーバーから360バイト受信D/AudioDecoder:8192バイトデコード
ネットワークコードはデータを結合しています。 369バイトの圧縮データを取得しましたが、受信側では1024バイトになりました。これらの1024バイトは、2つの全体フレームと1つの部分フレームで構成されています。 2つのフレーム全体がそれぞれ4096バイトにデコードされ、合計で8192バイトになります。残りの部分フレームは、十分なデータをデコーダに送信するとおそらくデコードされますが、通常はフレーム全体のみをデコーダに送信する必要があります。
さらに、MediaCodec.dequeueOutputBuffer()
は(正の)バッファーインデックスだけでなく、(負の)ステータスコードも返します。可能なコードの1つは_MediaCodec.INFO_OUTPUT_FORMAT_CHANGED
_です。これは、MediaCodec.getOutputFormat()
を呼び出してオーディオデータの形式を取得する必要があることを示します。入力がモノラルであっても、コーデックの出力ステレオが表示される場合があります。投稿したコードは、これらのステータスコードのいずれかを受け取ると、単にループから抜け出します。
私はあなたのソースでテストしました。いくつかのポイントがあります。
ビットレートはKの自然数ですが、コンピューターKではありません。64k= 64000ですが、64 * 1024ではありません
いくつかの変数を共有する長いコードを書くことは推奨されません。 A.エンコーダースレッドとデコーダースレッドを2つの独立したクラスに分離します。 B. DatagramSocketは送信者と受信者によって共有されますが、良くありません。
オーディオ形式を列挙するには、さらに値が必要です。つまり、サンプルレートは8000、11025、22050、44100から選択する必要があります