Подтвердить что ты не робот

Декодирование аппаратной кодированной камеры H264 с помощью ffmpeg в режиме реального времени

Я пытаюсь использовать аппаратное кодирование H264 на Android для создания видео с камеры и использовать FFmpeg для мультиплексора в аудио (все на телефоне Android)

То, что я до сих пор выполнил, - это пакетирование видео H264 в rtsp и декодирование с использованием VLC (более UDP), поэтому я знаю, что видео, по крайней мере, правильно отформатировано. Однако у меня возникают проблемы с получением видеоданных на FFmpeg в формате, который он может понять.

Я попытался отправить те же пакеты rtsp на порт 5006 на локальном хосте (через UDP), затем предоставив FFmpeg с файлом sdp, который сообщает ему, в каком локальном порту находится видеопоток, и как декодировать видео, если я правильно понимаю rtsp. Однако это не работает, и мне не удается диагностировать причину, так как FFmpeg просто сидит там, ожидая ввода.

По соображениям латентности и масштабируемости я не могу просто отправить видео и аудио на сервер и мультиплексировать там, это нужно сделать по телефону в максимально возможной степени.

Я думаю, что я ищу предложения о том, как это можно сделать. Оптимальным решением будет отправка пакетированного H264 видео в FFmpeg по каналу, но тогда я не могу отправить FFmpeg параметры файла sdp, необходимые для декодирования видео.

Я могу предоставить дополнительную информацию по запросу, например, как FFmpeg скомпилирован для Android, но я сомневаюсь, что это необходимо.

О, и как я начинаю FFmpeg через командную строку, я бы предпочел бы избегать mucking с jni, если это вообще возможно.

И спасибо за помощь, спасибо.

4b9b3361

Ответ 1

Вы пытались использовать 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();

Затем вы пишете на stdout и читаете из stdin и stderr. Это не труба, но она должна быть лучше, чем использование сетевого интерфейса.

Ответ 2

Немного поздно, но я думаю, что это хороший вопрос, и у него пока нет хорошего ответа.

Если вы хотите передать камеру и микрофон с устройства Android, у вас есть две основные альтернативы: реализация Java или NDK.

  • Реализация Java.

    Я только упомянул об этой идее, но в основном она реализует RTSP-сервер и протокол RTP в Java на основе этих стандартов Протокол потоковой передачи в реальном времени Версия 2.0 и Формат полезной нагрузки RTP для видео H.264. Эта задача будет очень долгой и трудной. Но если вы делаете свой PhP, было бы неплохо иметь приятную RTSP Java lib для Android.

  • Реализация NDK.

    Это альтернатива включает различные решения. Основная идея - использовать мощную C или С++ библиотеку в нашем приложении для Android. Для этого примера FFmpeg. Эта библиотека может быть скомпилирована для Android и может поддерживать различные архитектуры. Проблема такого подхода в том, что вам может понадобиться узнать об Android NDK, C и С++, чтобы выполнить это.

    Но есть альтернатива. Вы можете обернуть библиотеку c и использовать FFmpeg. Но как?

    Например, используя FFmpeg Android, который был скомпилирован с x264, libass, fontconfig, freetype и fribidi и поддерживает различные архитектуры. Но по-прежнему сложно запрограммировать, если вы хотите транслировать в реальном времени, вам нужно иметь дело с файловыми дескрипторами и потоками ввода/вывода.

    Лучшей альтернативой, с точки зрения программирования на Java, является использование JavaCV. JavaCV использует обертки из широко используемых библиотек компьютерного видения, которые включают: (OpenCV, FFmpeg и т.д., а также предоставляет классы служебных программ, чтобы упростить их функционирование на платформе Java, включая (конечно) Android.

    JavaCV также оснащен аппаратным ускоренным полноэкранным отображением изображений (CanvasFrame и GLCanvasFrame), простыми в использовании способами параллельного выполнения кода на нескольких ядрах (Parallel), удобной для пользователя геометрией и цветом калибровка камер и проекторов (GeometricCalibrator, ProCamGeometricCalibrator, ProCamColorCalibrator), обнаружение и сопоставление точек функций (ObjectFinder), набор классов, которые реализуют прямое выравнивание изображений систем проектора (в основном GNImageAligner, ProjectiveTransformer, ProjectiveColorTransformer, ProCamTransformer и ReflectanceInitializer), пакет анализа blob (Blobs), а также различные функциональные возможности в классе JavaCV. Некоторые из этих классов также имеют экземпляр OpenCL и OpenGL, их имена заканчиваются на CL или начинаются с GL, т.е.: JavaCVCL, GLCanvasFrame и т.д.

Но как мы можем использовать это решение?

Здесь у нас есть базовая реализация для потока с использованием 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);

Эта часть кода показывает, как инициализировать объект FFmpegFrameRecorder, называемый рекордером. Этот объект будет захватывать и кодировать кадры, полученные с камеры, и образцы, полученные из микрофона.

Если вы хотите захватить предварительный просмотр в том же приложении для Android, нам необходимо реализовать класс CameraPreview, этот класс будет преобразовывать необработанные данные, обслуживаемые с камеры, и он будет создавать Preview и Frame для FFmpegFrameRecorder.

Не забудьте заменить ip_destination на ip компьютера или устройства, куда вы хотите отправить поток. Например, порт может быть 8080.

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

Этот метод показывает реализацию метода onCameraFrame, который получает Mat (изображение) с камеры и преобразуется в виде кадра и записывается объектом FFmpegFrameRecorder.

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

То же самое со звуком audioData является объектом ShortBuffer, который будет записывать с помощью FFmpegFrameRecorder.

В ПК или устройстве вы можете запустить следующую команду для получения потока.

ffplay udp://ip_source:port

ip_source - это ip смартфона, который транслирует камеру и микрофонный поток. Порт должен быть тем же 8080.

Я создал решение в моем репозитории github здесь: UDPAVStreamer.

Удачи.