Der H. 264 avc-codiertes video von MediaCodec in Android kann nicht gespielt werden
HINTERGRUND:
Arbeite ich an der Umsetzung einer Rebe wie video-recorder für zwei Tage. Zuerst versuchte ich den MediaRecorder. Aber das video, das ich brauchen kann aus, indem Sie kleine video-clips. Diese Klasse kann nicht verwendet werden, um aufzuzeichnen einen kurzen video-clip. Dann fand ich den MediaCodec, FFmpeg und JavaCV. FFmpeg und JavaCV dieses problem lösen könnte. Aber ich habe zu kompilieren meines Projekts mit viele library-Dateien. Es erzeugt eine sehr große APK-Datei. Also ich bevorzuge die Umsetzung von MediaCodec, obwohl diese Klasse kann nur verwendet werden, nachdem Android 4.1. 90% Prozent der Benutzer zufrieden sein werden.
ERGEBNIS:
Habe ich endlich die kodierte Datei, aber es kann nicht gespielt werden. Ich habe die Informationen von FFprobe, das Ergebnis ist, wie:
Input #0, h264, aus " test.mp4':
Dauer: N/A bitrate: N/A
Stream #0:0: Video: h264 (Baseline), yuv420p, 640x480, 25 fps, 25 tbr, 1200k tbn, 50 tbc
Ich weiß nicht viel über den Mechanismus der H. 264 Codierung.
CODE:
Modifiziert von diesem link
public class AvcEncoder {
private static String TAG = AvcEncoder.class.getSimpleName();
private MediaCodec mediaCodec;
private BufferedOutputStream outputStream;
private int mWidth, mHeight;
private byte[] mDestData;
public AvcEncoder(int w, int h) {
mWidth = w;
mHeight = h;
Log.d(TAG, "Thread Id: " + Thread.currentThread().getId());
File f = new File("/sdcard/videos/test.mp4");
try {
outputStream = new BufferedOutputStream(new FileOutputStream(f));
Log.i("AvcEncoder", "outputStream initialized");
} catch (Exception e) {
e.printStackTrace();
}
try {
mediaCodec = MediaCodec.createEncoderByType("video/avc");
} catch (IOException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", w,
h);
mediaFormat.setInteger(MediaFormat.KEY_BIT_RATE, 2000000);
mediaFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 15);
//mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
//MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar);
mDestData = new byte[w * h
* ImageFormat.getBitsPerPixel(ImageFormat.YV12) / 8];
mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5);
mediaCodec.configure(mediaFormat, null, null,
MediaCodec.CONFIGURE_FLAG_ENCODE);
mediaCodec.start();
}
public void close() {
try {
mediaCodec.stop();
mediaCodec.release();
mediaCodec = null;
//outputStream.flush();
outputStream.close();
} catch (IOException e) {
}
}
public void offerEncoder(byte[] input) {
try {
CameraUtils.transYV12toYUV420Planar(input, mDestData, mWidth,
mHeight);
ByteBuffer[] inputBuffers = mediaCodec.getInputBuffers();
ByteBuffer[] outputBuffers = mediaCodec.getOutputBuffers();
int inputBufferIndex = mediaCodec.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0) {
ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
inputBuffer.clear();
inputBuffer.put(mDestData);
mediaCodec.queueInputBuffer(inputBufferIndex, 0,
mDestData.length, 0, 0);
}
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo,
0);
while (outputBufferIndex >= 0) {
ByteBuffer outputBuffer = outputBuffers[outputBufferIndex];
byte[] outData = new byte[bufferInfo.size];
outputBuffer.get(outData);
try {
outputStream.write(outData, 0, outData.length);
} catch (Exception e) {
Log.d("AvcEncoder", "Outputstream write failed");
e.printStackTrace();
}
//Log.i("AvcEncoder", outData.length + " bytes written");
mediaCodec.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo,
0);
}
} catch (Throwable t) {
t.printStackTrace();
}
}
}
Aufrufen dieser Klasse von Kamera startPreview:
private void startPreview() {
if (mCamera == null) {
return;
}
try {
mCamera.setPreviewDisplay(mSurfaceView.getHolder());
Parameters p = mCamera.getParameters();
Size s = p.getPreviewSize();
int len = s.width * s.height
* ImageFormat.getBitsPerPixel(p.getPreviewFormat()) / 8;
mAvcEncoder = new AvcEncoder(s.width, s.height);
mCamera.addCallbackBuffer(new byte[len]);
mCamera.setPreviewCallbackWithBuffer(new PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
mAvcEncoder.offerEncoder(data);
mCamera.addCallbackBuffer(data);
}
});
mCamera.startPreview();
} catch (IOException e) {
e.printStackTrace();
}
}
Schließen, wenn release Kamera:
private void releaseCamera() {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
if (mAvcEncoder != null) {
mAvcEncoder.close();
}
}
Du musst angemeldet sein, um einen Kommentar abzugeben.
Sparen Sie eine raw-H. 264-stream. Sie sollten konvertieren .mp4-format. Der einfachste Weg dies zu tun ist mit der MediaMuxer - Klasse (API 18+).
Finden Sie ein einfaches Beispiel auf bigflake und vollständiger Beispiele in Grafika.
Müssen Sie Präsentations-Zeitstempel für jedes Einzelbild. Sie können entweder erzeugen Sie nach Ihren gewünschten frame-rate (wie die bigflake Beispiel) oder erwerben Sie von der Quelle (wie die Kamera-input Beispiele in Grafika).
Edit: Für pre-API-18-Geräten (Android 4.1/4.2), MediaCodec ist viel schwieriger, mit zu arbeiten. Sie können nicht verwenden, Oberfläche, Eingabe-oder MediaMuxer, und das fehlen von Plattform-tests führte zu einigen unglücklichen Inkompatibilitäten. Diese Antwort hat eine übersicht.
In Ihrem speziellen Fall, werde ich beachten Sie, dass Ihr Beispiel-code versucht, geben Sie das input-format, aber das hat keine Wirkung -- die den AVC-codec definiert, welche input-Formate es akzeptiert, und muss Ihre app Abfragen. Sie werden wahrscheinlich feststellen, dass die Farben im encodierten video sind derzeit ist falsch, wie die Kamera und MediaCodec habe keine Farb-Formate im Allgemeinen (siehe die Antwort für die Farbe-swap-code).
Glaube ich, dass die Daten, die Sie speichern, ist roh h.264 Daten. Auch durch Sie sind die Benennung der Datei mit einem .mp4-Erweiterung, die Daten nicht in einem video-container, wie die von einem .mp4-Datei. Dies ist, warum die meisten media-Player nicht in der Lage sein, um die Datei zu spielen.
Haben Sie vielleicht Erfolg mit vlc, wenn man die raw-Daten-Datei ein .h264 oder .264-Erweiterung. Sie sollten versuchen, diese zu überprüfen, dass die Daten, die Sie erhalten, raw-h.264 Daten und, dass er gültig ist.
Außerdem kann Es helfen, das Lesen dieses alten thread:
Dekodierung von Raw-H264-stream in android?
In der Diskussion wird es Gespräche über die SPS (Sequence Parameter Set). Bei der Verarbeitung von streaming-live-video von einer Kamera, die ich zuvor hatte, zum dynamischen einfügen von der SPS an den Beginn der raw-h.264 Daten, um Sie zu verarbeiten. Werfen Sie einen Blick auf die raw-h.264 binäre Daten, die Sie speichern, um zu sehen, ob es hat den SPS-Rekord an den start.