Décoder le flux de caméra codé H264 d'android en utilisant ffmpeg en temps réel

je suis en train d'utiliser le matériel H264 codeur sur Android pour créer la vidéo de la caméra, et utiliser FFmpeg pour mux audio (tous sur le téléphone Android lui-même)

ce que j'ai accompli jusqu'à présent c'est emballer le H264 la vidéo dans rtsp paquets, et le décodage à l'aide de VLC (sur UDP), donc je sais que la vidéo est au moins formaté correctement. Cependant, j'ai du mal à obtenir les données vidéo à ffmpeg dans un format qu'il peut comprendre.

j'ai essayé envoyer le même rtsp paquets vers un port 5006 sur localhost( sur UDP), puis ffmpegsdp fichier qui lui indique le port local sur lequel le flux vidéo arrive et comment décoder la vidéo, si je comprends rtsp streaming correct. Cependant cela ne fonctionne pas et j'ai de la difficulté à diagnostiquer pourquoi, comme ffmpeg reste assis là à attendre la réponse.

pour des raisons de latence et d'évolutivité Je ne peux pas simplement envoyer la vidéo et l'audio au serveur et le mux là, il faut le faire au téléphone, de la manière la plus légère possible.

ce que je cherche, c'est des suggestions sur la façon d'y arriver. La solution optimale serait d'envoyer le packetized H264 vidéo ffmpeg un tuyau, mais je ne peux pas envoyer ffmpegsdp paramètres du fichier dont il a besoin pour décoder la vidéo.

je peux fournir plus d'informations sur demande, comme la façon dont ffmpeg est compilé pour Android mais je doute que nécessaire.

Oh, et j'ai commencer ffmpeg est via la ligne de commande, je préférerais éviter de coucher avec jni si c'est possible.

Et de l'aide serait apprécié, merci.

28
demandé sur HitOdessit 2011-10-12 22:06:58

2 réponses

avez-vous essayé java?lang.Runtime?

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

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

puis vous écrivez à stdout et lisez de stdin et stderr. Ce n'est pas un tuyau, mais ça devrait être mieux que d'utiliser une interface réseau.

1
répondu Douglas Jones 2012-10-31 03:42:02

un peu en retard mais je pense que c'est une bonne question et elle n'a pas encore de bonne réponse.

si vous voulez diffuser la caméra et le micro à partir d'un appareil android, vous avez deux alternatives principales: les implémentations Java ou NDK.

  1. Java mise en œuvre.

    je vais seulement mentionner l'idée mais fondamentalement c'est implémenter un serveur RTSP et un protocole RTP en java basé sur ces normes Protocole De Diffusion En Temps Réel Version 2.0 et RTP Payload Format for H. 264 Video. Cette tâche sera très longue et difficile. Mais si vous faites votre PhP il pourrait être agréable d'avoir une bonne RTSP Java lib pour Android.

  2. NDK mise en œuvre.

    il s'agit d'une alternative comprenant diverses solutions. L'idée principale est d'utiliser une bibliothèque power C ou C++ dans notre application Android. Pour cette instance, FFmpeg. Cette bibliothèque peut être compilée pour Android et peut prendre en charge divers architecture. Le problème de cette approche est que vous pourriez avoir besoin d'en savoir plus sur L'Android NDK, C et C++ pour accomplir ceci.

    mais il y a une alternative. Vous pouvez envelopper la bibliothèque c et utiliser le FFmpeg. Mais comment?

    par exemple, en utilisant FFmpeg Android, qui a été compilé avec x264, libass, fontconfig, freetype et fribidi et supporte différentes architectures. Mais il est toujours difficile de programmer le si vous voulez diffuser en temps réel, vous devez Gérer les descripteurs de fichiers et les flux entrants et sortants.

    la meilleure alternative, du point de vue de la programmation Java, est d'utiliser JavaCV. JavaCV utilise des enveloppes provenant de bibliothèques de vision par ordinateur couramment utilisées qui comprennent: ( OpenCV, FFmpeg, etc, et fournit des classes d'utilités pour rendre leur fonctionnalité plus facile à utiliser sur la plate-forme Java, y compris (bien sûr) Android.

    JavaCV est également livré avec le matériel accéléré plein écran à l'affichage de l'image (CanvasFrame et GLCanvasFrame), des méthodes faciles à utiliser pour exécuter le code en parallèle sur plusieurs cœurs (Parallel), calibrage géométrique et colorimétrique convivial des caméras et des projecteurs (GeometricCalibrator,ProCamGeometricCalibrator,ProCamColorCalibrator), la détection et l'appariement des points caractéristiques (ObjectFinder), un ensemble de classes qui implémentent l'alignement direct des images des systèmes projecteur-caméra (principalement GNImageAligner,ProjectiveTransformer,ProjectiveColorTransformer,ProCamTransformer et ReflectanceInitializer), un paquet d'analyse blob (Blobs), ainsi que fonctionnalités diverses dans le JavaCV classe. Certaines de ces classes ont aussi une contrepartie OpenCL et OpenGL, leurs noms se terminant par CL ou en commençant par GL, i.e.:JavaCVCL,GLCanvasFrame, etc.

Mais comment pouvons-nous utiliser cette solution?

ici nous avons une implémentation de base à diffuser en utilisant 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);

cette partie du code montre comment initialiser L'objet FFmpegFrameRecorder appelé recorder. Cet objet sera capturer et coder les cadres obtenus de la caméra et les échantillons obtenus du microphone.

si vous voulez capturer un aperçu dans la même application Android, alors nous avons besoin de mettre en œuvre une classe CameraPreview cette classe convertira les données brutes servies à partir de la caméra et il créera L'aperçu et le cadre pour le FFmpegFrameRecorder.

N'oubliez pas de remplacer l'ip_destination par l'ip du pc ou du périphérique où vous voulez envoyer le flux. Le port peut être 8080 comme exemple.

@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;
}

cette méthode montre la mise en oeuvre de la onCameraFrame méthode qui récupère le tapis (image) de la caméra et il est converti en Image et enregistré par L'objet Ffmpegramerecorder.

@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();
    }
}

Même avec l'audio audioData est un ShortBuffer objet qui sera enregistré par le FFmpegFrameRecorder.

dans le PC ou la destination de l'appareil, vous pouvez exécuter la commande suivante pour obtenir le flux.

ffplay udp://ip_source:port

ip_source est l'ip du smartphone qui diffuse la caméra et le flux mic. Le port doit être le même 8080.

j'ai créé une solution dans mon dépôt GitHub ici:UDPAVStreamer.

Bonne chance

1
répondu Teocci 2017-12-06 10:08:46