Декодирование аппаратной кодированной камеры 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, если это вообще возможно.
И спасибо за помощь, спасибо.
Ответы
Ответ 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.
Удачи.