Uso la classe MediaCodec
fornita da Android SDK dal livello API 16 con il codificatore OMX.SEC.aac.enc
per codificare l'audio in un file. Ottengo l'input audio dalla classe AudioRecord
. La mia istanza della classe AudioRecord
è configurato in questo modo:Codificatore AAC Android MediaCodec
bufferSize = AudioRecord.getMinBufferSize(44100, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT);
recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, 44100, AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_DEFAULT, bufferSize);
Sono in grado di riprodurre i dati grezzi dall'istanza AudioRecord
, quindi il problema non risiede lì.
Scrivo l'uscita dall'istanza AudioRecord
a un'istanza ByteBuffer
e la passiamo a un buffer di input disponibile dall'encoder. L'uscita dal codificatore viene scritta su un file sulla scheda SD.
Questi sono i parametri di configurazione per il mio MediaCodec
esempio:
codec = MediaCodec.createEncoderByType("audio/mp4a-latm");
MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
format.setInteger(MediaFormat.KEY_BIT_RATE, 64 * 1024);
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 2);
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 44100);
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectHE);
codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
VLC mi dice che non ci sono corsi d'acqua nel mio file AAC. Il comando FFMPEG -i @[email protected]
mi dà il seguente errore: Dati non validi trovati durante l'elaborazione dell'ingresso. Nessuno dei mediaplayer che ho provato è in grado di riprodurre il mio file.
Perché non riesco a riprodurre il mio file? Non ricevo errori OpenMAX
in LogCat
e l'applicazione non si arresta in modo anomalo durante la codifica. Ho scritto un codificatore video che funziona sullo stesso principio e funziona.
Questo è il codice per leggere i dati da l'istanza AudioRecord
in un buffer:
new Thread() {
public void run() {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(bufferSize);
int read = 0;
while (isRecording) {
read = recorder.read(byteBuffer, bufferSize);
if(AudioRecord.ERROR_INVALID_OPERATION != read){
encoder.add(byteBuffer);
}
}
recorder.stop();
}
}.start();
la funzione Add dalle mie copie encoder il contenuto di un buffer all'altro:
public void add(ByteBuffer input) {
if (!isRunning)
return;
if (tmpInputBuffer == null)
tmpInputBuffer = ByteBuffer.allocate(input.capacity());
if (!tmpBufferClear)
Log.e("audio encoder", "deadline missed"); //TODO lower bit rate
synchronized (tmpInputBuffer) {
tmpInputBuffer.clear();
tmpInputBuffer.put(input);
tmpInputBuffer.notifyAll();
Log.d("audio encoder", "pushed data into tmpInputBuffer");
}
}
Il il seguente codice viene utilizzato per occupare il buffer di ingresso dell'encoder:
new Thread() {
public void run() {
while (isRunning) {
if (tmpInputBuffer == null)
continue;
synchronized (tmpInputBuffer) {
if (tmpBufferClear) {
try {
Log.d("audio encoder", "falling asleep");
tmpInputBuffer.wait(); //wait when no input is available
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
ByteBuffer[] inputBuffers = codec.getInputBuffers();
int inputBufferIndex;
do
inputBufferIndex = codec.dequeueInputBuffer(-1);
while (inputBufferIndex < 0);
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
Log.d("input buffer size", String.valueOf(inputBuffer.capacity()));
Log.d("tmp input buffer size", String.valueOf(tmpInputBuffer.capacity()));
inputBuffer.put(tmpInputBuffer.array());
tmpInputBuffer.clear();
codec.queueInputBuffer(inputBufferIndex, 0, tmpInputBuffer.capacity(), 0, 0);
tmpBufferClear = true;
Log.d("audio encoder", "added to input buffer");
}
}
}
}.start();
scrivo l'uscita dal codificatore in un file locale come questo:
new Thread() {
public void run() {
while (isRunning) {
ByteBuffer[] outputBuffers = codec.getOutputBuffers();
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, -1);
while (outputBufferIndex >= 0) {
ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
byte[] outData = new byte[bufferInfo.size];
outputBuffer.get(outData);
try {
fileWriter.write(outData, 0, outData.length);
}
catch (IOException e) {
e.printStackTrace();
}
codec.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = codec.dequeueOutputBuffer(bufferInfo, 0);
Log.d("audio encoder", "removed from output buffer");
}
}
codec.stop();
try {
fileWriter.close();
}
catch (IOException e) {
e.printStackTrace();
}
}
}.start();
tmpBufferClear = true;
Log.d("audio encoder", "added to input buffer");
}
}
}
}.start();
pubblica il metodo completo in cui lo fai. con start() stop() .. grazie. –
Inoltre, dai un'occhiata a questo thread - https://code.google.com/p/spydroid-ipcamera/issues/detail?id=43 sembra che abbiano un pezzo di codice funzionante per registrare AAC lì. –
@Sergey Benner, Non usano la classe MediaCodec. La classe MediaRecorder è usata in Spydroid. Quella classe sarà la mia seconda scelta se la classe MediaCodec non funziona. –