28 Stimmen

Dekodieren des hardwarekodierten H264-Kamerafeeds von Android mit ffmpeg in Echtzeit

Ich versuche, die Hardware zu benutzen H264 Encoder auf Android, um Videos von der Kamera zu erstellen, und verwenden FFmpeg Audio muxen (alles auf dem Android-Telefon selbst)

Was ich bis jetzt erreicht habe, ist die Paketierung der H264 Video in rtsp Pakete und dekodiert sie mit VLC (über UDP ), so dass ich weiß, dass das Video zumindest richtig formatiert ist. Ich habe jedoch Probleme, die Videodaten in die ffmpeg in einem Format, das es verstehen kann.

Ich habe versucht, das gleiche zu senden rtsp Pakete an einen Port 5006 auf localhost (über UDP), und dann die Bereitstellung ffmpeg mit dem sdp Datei, die ihm mitteilt, an welchem lokalen Port der Videostream ankommt und wie das Video zu dekodieren ist, wenn ich das richtig verstehe rtsp richtig streamen. Dies funktioniert jedoch nicht, und ich habe Schwierigkeiten, den Grund dafür herauszufinden, da ffmpeg sitzt einfach da und wartet auf Eingaben.

Aus Gründen der Latenz und der Skalierbarkeit kann ich Video und Audio nicht einfach an den Server senden und dort muxen, sondern muss dies auf dem Telefon tun, und zwar so einfach wie möglich.

Ich bin auf der Suche nach Vorschlägen, wie man dies bewerkstelligen kann. Die optimale Lösung wäre das Senden der paketierten H264 Video zu ffmpeg über eine Leitung, aber dann kann ich nicht senden ffmpeg die sdp Dateiparameter, die für die Dekodierung des Videos erforderlich sind.

Auf Anfrage kann ich weitere Informationen zur Verfügung stellen, z. B. wie ffmpeg für Android kompiliert ist, aber ich bezweifle, dass das notwendig ist.

Oh, und die Art, wie ich anfange ffmpeg ist über die Kommandozeile, ich würde es wirklich lieber vermeiden, mit jni herumzupfuschen, wenn das überhaupt möglich ist.

Und Hilfe wäre sehr willkommen, danke.

1voto

Douglas Jones Punkte 2432

Haben Sie versucht, java.lang.Runtime zu verwenden?

String[] parameters = {"ffmpeg", "other", "args"};
Program program Runtime.getRuntime().exec(parameters);

InputStream in = program.getInputStream();
OutputStream out = program.getOutputStream();
InputStream err = program.getErrorStream();

Dann schreiben Sie nach stdout und lesen von stdin und stderr. Es ist keine Pipe, aber es sollte besser sein als eine Netzwerkschnittstelle.

1voto

Teocci Punkte 5144

Etwas spät, aber ich denke, das ist eine gute Frage, auf die es noch keine gute Antwort gibt.

Wenn Sie die Kamera und das Mikrofon von einem Android-Gerät streamen möchten, haben Sie zwei Möglichkeiten: Java- oder NDK-Implementierungen.

  1. Java-Implementierung.

Ich werde nur die Idee erwähnen, aber im Grunde geht es darum, einen RTSP-Server und ein RTP-Protokoll in Java zu implementieren, das auf diesen Standards basiert Echtzeit-Streaming-Protokoll Version 2.0 y RTP-Nutzdatenformat für H.264-Video . Diese Aufgabe wird sehr lang und hart sein. Aber wenn Sie Ihre PhP tun, könnte es schön sein, eine schöne RTSP Java lib für Android zu haben.

  1. NDK-Implementierung.

Diese Alternative umfasst verschiedene Lösungen. Die Hauptidee ist, eine leistungsstarke C- oder C++-Bibliothek in unserer Android-Anwendung zu verwenden. In diesem Fall ist das FFmpeg. Diese Bibliothek kann für Android kompiliert werden und kann verschiedene Architekturen unterstützen. Das Problem bei diesem Ansatz ist, dass Sie sich möglicherweise mit dem Android NDK, C und C++ vertraut machen müssen, um dies zu erreichen.

Aber es gibt eine Alternative. Sie können die C-Bibliothek einpacken und FFmpeg verwenden. Aber wie?

Zum Beispiel mit FFmpeg Android , das mit x264, libass, fontconfig, freetype und fribidi kompiliert wurde und verschiedene Architekturen unterstützt. Aber es ist immer noch schwer zu programmieren, wenn man in Echtzeit streamen will, muss man sich mit Dateideskriptoren und In/Out-Streams beschäftigen.

Die beste Alternative aus Sicht der Java-Programmierung ist die Verwendung von JavaCV . JavaCV verwendet Wrapper von häufig verwendeten Bibliotheken der Computer Vision, die umfasst: ( OpenCV , FFmpeg usw., und bietet Dienstleistungsklassen, um ihre Funktionalität auf der Java-Plattform, einschließlich (natürlich) Android, einfacher zu nutzen.

JavaCV bietet auch eine hardwarebeschleunigte Vollbildanzeige ( CanvasFrame y GLCanvasFrame ), einfach zu verwendende Methoden zur parallelen Ausführung von Code auf mehreren Kernen ( Parallel ), benutzerfreundliche Geometrie- und Farbkalibrierung von Kameras und Projektoren ( GeometricCalibrator , ProCamGeometricCalibrator , ProCamColorCalibrator ), Erkennung und Abgleich von Merkmalspunkten ( ObjectFinder ), eine Reihe von Klassen, die die direkte Bildausrichtung von Projektor-Kamera-Systemen implementieren (hauptsächlich GNImageAligner , ProjectiveTransformer , ProjectiveColorTransformer , ProCamTransformer y ReflectanceInitializer ), ein Blob-Analysepaket ( Blobs ), sowie verschiedene Funktionen in der JavaCV Klasse. Einige dieser Klassen haben auch ein OpenCL- und OpenGL-Gegenstück, ihre Namen enden mit CL oder beginnend mit GL d.h.: JavaCVCL , GLCanvasFrame etc.

Aber wie können wir diese Lösung nutzen?

Hier haben wir eine grundlegende Implementierung des Streamings über UDP.

String streamURL = "udp://ip_destination:port";
recorder = new FFmpegFrameRecorder(streamURL, frameWidth, frameHeight, 1);
recorder.setInterleaved(false);
// video options //
recorder.setFormat("mpegts");
recorder.setVideoOption("tune", "zerolatency");
recorder.setVideoOption("preset", "ultrafast");
recorder.setVideoBitrate(5 * 1024 * 1024);
recorder.setFrameRate(30);
recorder.setSampleRate(AUDIO_SAMPLE_RATE);
recorder.setVideoCodec(AV_CODEC_ID_H264);
recorder.setAudioCodec(AV_CODEC_ID_AAC);

Dieser Teil des Codes zeigt, wie man das FFmpegFrameRecorder-Objekt namens recorder initialisiert. Dieses Objekt wird die von der Kamera erhaltenen Bilder und die vom Mikrofon erhaltenen Samples erfassen und kodieren.

Wenn Sie eine Vorschau in der gleichen Android-App erfassen möchten, müssen wir eine CameraPreview-Klasse implementieren. Diese Klasse konvertiert die von der Kamera gelieferten Rohdaten und erstellt die Vorschau und den Rahmen für den FFmpegFrameRecorder.

Denken Sie daran, ip_destination durch die IP des PCs oder Geräts zu ersetzen, an das Sie den Stream senden möchten. Der Port kann zum Beispiel 8080 sein.

@Override
public Mat onCameraFrame(Mat mat)
{
    if (audioRecordRunnable == null) {
        startTime = System.currentTimeMillis();
        return mat;
    }
    if (recording && mat != null) {
        synchronized (semaphore) {
            try {
                Frame frame = converterToMat.convert(mat);
                long t = 1000 * (System.currentTimeMillis() - startTime);
                if (t > recorder.getTimestamp()) {
                    recorder.setTimestamp(t);
                }
                recorder.record(frame);
            } catch (FFmpegFrameRecorder.Exception e) {
                LogHelper.i(TAG, e.getMessage());
                e.printStackTrace();
            }
        }
    }
    return mat;
}

Diese Methode zeigt die Implementierung der onCameraFrame Methode, die das Mat (Bild) von der Kamera erhält und es als Frame konvertiert und durch das FFmpegFrameRecorder-Objekt aufgezeichnet wird.

@Override
public void onSampleReady(ShortBuffer audioData)
{
    if (recorder == null) return;
    if (recording && audioData == null) return;

    try {
        long t = 1000 * (System.currentTimeMillis() - startTime);
        if (t > recorder.getTimestamp()) {
            recorder.setTimestamp(t);
        }
        LogHelper.e(TAG, "audioData: " + audioData);
        recorder.recordSamples(audioData);
    } catch (FFmpegFrameRecorder.Exception e) {
        LogHelper.v(TAG, e.getMessage());
        e.printStackTrace();
    }
}

Das Gleiche gilt für den Ton der audioData ist eine ShortBuffer Objekt, das vom FFmpegFrameRecorder aufgezeichnet werden soll.

Auf dem Ziel-PC oder -Gerät können Sie den folgenden Befehl ausführen, um den Stream abzurufen.

ffplay udp://ip_source:port

El ip_source ist die IP des Smartphones, das den Kamera- und Mikrofon-Stream überträgt. Der Port muss derselbe sein: 8080.

Ich habe eine Lösung in meinem Github-Repository hier erstellt: UDPAVStreamer .

Viel Glück!

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X