Wie man bitmap (frames) aus Videos mit MediaCodec
Ich versuche, alle Bilder aus video-Datei mit MediaCodec. Wenn ich versuche ein video-display auf SurfaceView, ist alles ok. Aber wenn die Oberfläche null ist, und wenn ich versuche, zu bekommen Bitmap aus byte-array, alwaus erhalten null-oder Laufzeit-exception.
Dies ist mein code:
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();
}
}
Ich habe keine Ahnung wie es zu lösen.
Samos
InformationsquelleAutor user3577955 | 2014-04-27
Du musst angemeldet sein, um einen Kommentar abzugeben.
Gibt es ein paar Probleme mit Ihrem code (oder, wohl mit MediaCodec).
Erste, der Umgang mit ByteBuffer in MediaCodec ist schlecht, so müssen Sie manuell einstellen, die buffer-Parameter von den Werten in der BufferInfo Objekt, das gefüllt ist von
dequeueOutputBuffer()
.Zweiten, die Werte kommen aus dem MediaCodec sind im YUV-format, nicht RGB. Ab Android 4.4 das Android-framework bietet keine Funktion, die konvertiert die Ausgabe in Bitmap. Sie müssen Ihre eigenen YUV-zu-RGB-Wandler (plural-es sind mehrere Formate). Einige Geräte verwenden proprietäre, undokumentierte Farb-Formate.
Sehen Sie ein Beispiel für das extrahieren und untersuchen MediaCodec decoder-buffer-Inhalt in CTS EncodeDecodeTest Puffer-zu-Puffer-Methoden (z.B.
checkFrame()
).Ein zuverlässiger Weg zu gehen, dies ist zurück zu gehen, um die Dekodierung zu einer Oberfläche, aber extrahieren von Pixeln mit OpenGL ES. Die bigflake ExtractMpegFramesTest zeigt, wie dies zu tun.
InformationsquelleAutor fadden
Die kurze Antwort ist:
In der default-Abschnitt der switch-Anweisung, müssen Sie zum zurücksetzen der ByteBuffer position, so dass anstelle von:
solltest du so etwas wie
In deinem original-code, Sie werden feststellen, dass Ihre ByteBuffer hat eine Restlaufzeit() 0.
InformationsquelleAutor bremen_matt