Estoy tratando de conseguir todos los fotogramas del archivo de vídeo utilizando MediaCodec. Si trato de mostrar vídeo en SurfaceView, todo está bien. Pero si la superficie es nulo, y cuando intento obtener el mapa de bits de matriz de bytes, alwaus obtener nulo o excepción en tiempo de ejecución.

Este es mi código:

private class PlayerThread extends Thread {
private MediaExtractor extractor;
private MediaCodec decoder;
private Surface surface;
public PlayerThread(Surface surface) {
this.surface = surface;
}
@Override
public void run() {
extractor = new MediaExtractor();
extractor.setDataSource(videoPath);
for (int i = 0; i < extractor.getTrackCount(); i++) {
MediaFormat format = extractor.getTrackFormat(i);
String mime = format.getString(MediaFormat.KEY_MIME);
if (mime.startsWith("video/")) {
extractor.selectTrack(i);
decoder = MediaCodec.createDecoderByType(mime);
decoder.configure(format, /*surface*/ null, null, 0);
break;
}
}
if (decoder == null) {
Log.e("DecodeActivity", "Can't find video info!");
return;
}
decoder.start();
ByteBuffer[] inputBuffers = decoder.getInputBuffers();
ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
BufferInfo info = new BufferInfo();
boolean isEOS = false;
while (!Thread.interrupted()) {
++numFrames;
if (!isEOS) {
int inIndex = decoder.dequeueInputBuffer(10000);
if (inIndex >= 0) {
ByteBuffer buffer = inputBuffers[inIndex];
int sampleSize = extractor.readSampleData(buffer, 0);
if (sampleSize < 0) {
//We shouldn't stop the playback at this point,
//just pass the EOS
//flag to decoder, we will get it again from the
//dequeueOutputBuffer
Log.d("DecodeActivity",
"InputBuffer BUFFER_FLAG_END_OF_STREAM");
decoder.queueInputBuffer(inIndex, 0, 0, 0,
MediaCodec.BUFFER_FLAG_END_OF_STREAM);
isEOS = true;
} else {
decoder.queueInputBuffer(inIndex, 0, sampleSize,
extractor.getSampleTime(), 0);
extractor.advance();
}
}
}
int outIndex = decoder.dequeueOutputBuffer(info, 10000);
switch (outIndex) {
case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
Log.d("DecodeActivity", "INFO_OUTPUT_BUFFERS_CHANGED");
outputBuffers = decoder.getOutputBuffers();
break;
case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
Log.d("DecodeActivity",
"New format " + decoder.getOutputFormat());
break;
case MediaCodec.INFO_TRY_AGAIN_LATER:
Log.d("DecodeActivity", "dequeueOutputBuffer timed out!");
break;
default:
//I tried get Bitmap on few ways
//1.
//ByteBuffer buffer = outputBuffers[outIndex];
//byte[] ba = new byte[buffer.remaining()];
//buffer.get(ba);
//final Bitmap bmp = BitmapFactory.decodeByteArray(ba, 0, ba.length);//this return null
//2. 
//ByteBuffer buffer = outputBuffers[outIndex];
//final Bitmap bmp = Bitmap.createBitmap(1920, 1080, Config.ARGB_8888);//using MediaFormat object I know width and height
//int a = bmp.getByteCount(); //8294400
//buffer.rewind();
//int b = buffer.capacity();//3137536
//int c = buffer.remaining();//3137536
//bmp.copyPixelsFromBuffer(buffer); //java.lang.RuntimeException: Buffer not large enough for pixels
//I know what exception mean, but i don't know why xception occurs
//In this place I need bitmap
//We use a very simple clock to keep the video FPS, or the
//video
//playback will be too fast
while (info.presentationTimeUs / 1000 > System
.currentTimeMillis() - startMs) {
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
decoder.releaseOutputBuffer(outIndex, true);
break;
}
//All decoded frames have been rendered, we can stop playing
//now
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
Log.d("DecodeActivity",
"OutputBuffer BUFFER_FLAG_END_OF_STREAM");
break;
}
}
decoder.stop();
decoder.release();
extractor.release();
}
}

No tengo idea de cómo resolverlo.

Samos

InformationsquelleAutor user3577955 | 2014-04-27

2 Comentarios

  1. 9

    Hay un par de problemas con el código (o, posiblemente, con MediaCodec).

    Primero, el ByteBuffer manejo en MediaCodec es pobre, así que usted tiene que configurar manualmente el búfer de parámetros a partir de los valores en la BufferInfo objeto que es llenada por dequeueOutputBuffer().

    Segundo, los valores que salen de la MediaCodec están en formato YUV, no RGB. Como de Android 4.4, el Android framework no tiene una función que va a convertir la salida de mapa de bits. Usted tendrá que proporcionar su propia YUV a RGB convertidores (plural — hay varios formatos). Algunos dispositivos de uso privativo, los formatos de color.

    Puede ver un ejemplo de la extracción y examen de MediaCodec decodificador contenido del buffer en el CTS EncodeDecodeTest buffer a buffer métodos (por ejemplo,checkFrame()).

    De manera más fiable para ir sobre esto es volver a la decodificación de la Superficie, pero de extracto de los píxeles con OpenGL ES. El bigflake ExtractMpegFramesTest muestra cómo hacer esto.

  2. 2

    La respuesta corta es:

    Por defecto en la sección de la instrucción switch, usted necesita para restablecer la ByteBuffer posición, así que en lugar de:

    ByteBuffer buffer = outputBuffers[outIndex];
    byte[] ba = new byte[buffer.remaining()];
    buffer.get(ba); 

    usted debe tener algo como

    ByteBuffer buffer = outputBuffers[outIndex];
    buffer.position(info.offset);
    buffer.limit(info.offset + info.size);
    byte[] ba = new byte[buffer.remaining()];
    buffer.get(ba); 

    En su código original, usted encontrará que su ByteBuffer tiene un restantes() de 0.

Dejar respuesta

Please enter your comment!
Please enter your name here