Android MediaCodec mais lento no modo assíncrono do que no modo síncrono?
novamente uma pergunta minha sobre a classe MediaCodec de andróides. Consegui decodificar com sucesso o conteúdo bruto do h264 e exibir o resultado em dois TextureViews. O fluxo h264 vem de um servidor que está executando uma cena openGL. A cena tem uma câmera e, portanto, responde à entrada dos usuários. Para reduzir ainda mais a latência entre uma entrada no servidor e o resultado real no smartphone, pensei em usar o modo Async do MediaCodecs. Aqui está como eu configuro as duas variantes, síncrona e assíncrona:
Assíncrono:
//decoderCodec is "video/avc"
MediaFormat fmt = MediaFormat.createVideoFormat(decoderCodec, 1280,720);
codec.setCallback(new MediaCodec.Callback() {
@Override
public void onInputBufferAvailable(MediaCodec codec, int index) {
byte[] frameData;
try {
frameData = frameQueue.take(); //this call is blocking
} catch (InterruptedException e) {
return;
}
ByteBuffer inputData = codec.getInputBuffer(index);
inputData.clear();
inputData.put(frameData);
codec.queueInputBuffer(index, 0, frameData.length, 0, 0);
}
@Override
public void onOutputBufferAvailable(MediaCodec codec, int index, MediaCodec.BufferInfo info) {
codec.releaseOutputBuffer(index, true);
}
//The two other methods are left blank at the moment.
});
codec.configure(fmt, surface, null, 0);
codec.start();
Sincronizar: (é configurado como Async, exceto ocodec.setCallback(...)
parte. A classe em que ambas as variantes residem é Runnable.
public void run() {
while(!Thread.interrupted())
{
if(!IS_ASYNC) {
byte[] frameData;
try {
frameData = frameQueue.take(); //this call is blocking
} catch (InterruptedException e) {
break;
}
int inIndex = codec.dequeueInputBuffer(BUFFER_TIMEOUT);
if (inIndex >= 0) {
ByteBuffer input = codec.getInputBuffer(inIndex);
input.clear();
input.put(frameData);
codec.queueInputBuffer(inIndex, 0, frameData.length, 0, 0);
}
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outIndex = codec.dequeueOutputBuffer(bufferInfo, BUFFER_TIMEOUT);
if(outIndex >= 0)
codec.releaseOutputBuffer(outIndex, true);
}
else sleep(3000); //Just for testing, if we are in Async, this thread has nothing to do actually...
}
}
Ambas as abordagens funcionam, mas estou observando que os vídeos reproduzidos no modo síncrono são muito mais suaves e a latência também é menor.
Eu tive a ideia de usar o modo assíncrono porqueframeQueue
é umLinkedBlockingDeque
e pensei que, se o decodificador síncrono estiver aguardando demais para que novos dados do quadro cheguem, a saída decodificada já estará disponível, mas não será exibida devido à natureza de bloqueio da fila. Por outro lado, eu não queria fazer algo como ocupado esperando e pesquisando a fila, os inputBuffers e outputBuffers o tempo todo.
Então, tentei o AsyncMode usando os retornos de chamada, mas o resultado obtido é pior do que no modo síncrono. A pergunta para vocês agora é: Por quê? Uso mal o modo assíncrono ou é outra coisa?
Obrigado por qualquer feedback!
Christoph