【问题标题】:Decode android's hardware encoded H264 camera feed using ffmpeg in real time使用 ffmpeg 实时解码 android 的硬件编码 H264 摄像头馈送
【发布时间】:2011-12-06 08:48:22
【问题描述】:

我正在尝试使用 Android 上的硬件 H264 编码器从摄像头创建视频,并使用 FFmpeg 混合音频(全部在 Android 手机本身上)

到目前为止,我所做的是将H264 视频打包成rtsp 数据包,并使用VLC(通过UDP)对其进行解码,所以我知道视频至少格式正确。但是,我无法以它可以理解的格式将视频数据发送到 ffmpeg

我尝试将相同的 rtsp 数据包发送到 localhost 上的端口 5006(通过 UDP),然后向 ffmpeg 提供 sdp 文件,告诉它视频流进入哪个本地端口,如果我正确理解rtsp 流媒体,如何解码视频。但是这不起作用,我无法诊断原因,因为ffmpeg 只是坐在那里等待输入。

出于延迟和可扩展性的原因,我不能只将视频和音频发送到服务器并在那里多路复用,它必须在手机上以尽可能轻量级的方式完成。

我想我正在寻找的是关于如何实现这一点的建议。最佳解决方案是通过管道将打包的H264 视频发送到ffmpeg,但是我无法发送ffmpeg 解码视频所需的sdp 文件参数。

我可以根据要求提供更多信息,例如如何为 Android 编译 ffmpeg,但我怀疑这是必要的。

哦,我启动 ffmpeg 的方式是通过命令行,如果可能的话,我真的宁愿避免与 jni 混在一起。

非常感谢您的帮助,谢谢。

【问题讨论】:

  • 为什么要使用 ffmpeg 解码?使用内置的 MediaPlayer 对象
  • 您是否尝试过使用 live555 通过 RTSP 流式传输 ffmpeg 的输出?另外,ffmpeg 不应该探测流并找出流信息本身吗?
  • 我认为 Aviad 有它的真相。你怎么知道相机产生什么视频格式?
  • 内置媒体播放器不会打包视频以进行流式传输。我当时尝试开发的应用程序是一个视频会议应用程序。视频实际上不必在录制它的设备上播放 - 想法是将其直接发送到 FFMpeg 并使用 FFMpeg 作为一种媒体服务器,可以混合原始音频样本和视频帧 - 或破译保存的实时视频文件,这似乎更难。
  • 我猜libstreaming 完全符合您的需要。不幸的是,时间机器尚未通过 QA。

标签: android ffmpeg h.264 rtsp


【解决方案1】:

有点晚了,但我认为这是一个很好的问题,但还没有很好的答案。

如果您想从 Android 设备流式传输摄像头和麦克风,您有两种主要选择:Java 或 NDK 实现。

  1. Java 实现。

我只想提一下这个想法,但基本上它是基于这些标准Real-Time Streaming Protocol Version 2.0RTP Payload Format for H.264 Video 在java 中实现一个RTSP 服务器和RTP 协议。这项任务将非常漫长而艰巨。但是如果你正在做你的 PhP,那么有一个不错的 Android RTSP Java 库可能会很好。

  1. NDK 实施。

这是替代方案,包括各种解决方案。主要思想是在我们的 Android 应用程序中使用强大的 C 或 C++ 库。例如,FFmpeg。该库可以针对 Android 进行编译,并且可以支持各种架构。 这种方法的问题是您可能需要了解 Android NDK、C 和 C++ 才能完成此操作。

但是还有一个选择。您可以包装 c 库并使用 FFmpeg。但是怎么做呢?

例如使用FFmpeg Android,它已经用x264、libass、fontconfig、freetype和fribidi编译,支持各种架构。但是,如果您想实时流式传输,则仍然难以编程,您需要处理文件描述符和输入/输出流。

从 Java 编程的角度来看,最好的替代方法是使用 JavaCV。 JavaCV 使用来自常用计算机视觉库的包装器,包括:(OpenCVFFmpeg 等,并提供实用程序类以使其功能更易于在 Java 平台(当然包括 Android)上使用。

JavaCV 还带有硬件加速的全屏图像显示(CanvasFrameGLCanvasFrame)、在多核上并行执行代码的易于使用的方法(Parallel)、用户友好的几何和颜色相机和投影仪的校准(GeometricCalibratorProCamGeometricCalibratorProCamColorCalibrator),特征点的检测和匹配(ObjectFinder),一组实现投影仪-相机系统直接图像对齐的类(主要是GNImageAlignerProjectiveTransformerProjectiveColorTransformerProCamTransformerReflectanceInitializer)、blob 分析包 (Blobs),以及 JavaCV 类中的其他功能。其中一些类还具有 OpenCL 和 OpenGL 对应项,它们的名称以 CL 结尾或以 GL 开头,即:JavaCVCLGLCanvasFrame 等。

但是我们如何使用这个解决方案呢?

这里我们有一个使用 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);

这部分代码展示了如何初始化名为recorder的FFmpegFrameRecorder对象。该对象将捕获和编码从相机获得的帧和从麦克风获得的样本。

如果您想在同一个 Android 应用中捕获预览,那么我们需要实现一个 CameraPreview 类,该类将转换从相机提供的原始数据,并为 FFmpegFrameRecorder 创建预览和帧。

请记住将 ip_destination 替换为您要发送流的 pc 或设备的 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(图片)并将其转换为 Frame 并由 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 进行记录。

在 PC 或设备目标中,您可以运行以下命令来获取流。

ffplay udp://ip_source:port

ip_source 是智能手机的 IP,用于传输摄像头和麦克风流。端口必须是同一个8080。

我在我的 github 存储库中创建了一个解决方案:UDPAVStreamer

祝你好运

【讨论】:

    【解决方案2】:

    您是否尝试过使用 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();
    

    然后你写入标准输出并从标准输入和标准错误读取。它不是管道,但应该比使用网络接口更好。

    【讨论】:

    • OP 在这里,我不再处理这个问题(我放弃了),但我应该补充一点,我确实尝试过这个。我不记得所有细节,但我尝试同时使用 InputStream 和使用操作系统的多个 FIFO 管道(一个用于视频,一个用于音频)。然而,这种方法的问题是我无法提供足够的信息来理解和解码由摄像头馈送生成的视频数据包。我想使用 rtsp 的真正原因是它——理论上——会为 FFmpeg 提供足够的信息来解码直播流。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-02-23
    • 1970-01-01
    • 2012-10-08
    • 2014-09-25
    • 1970-01-01
    • 2016-09-13
    • 2013-08-12
    相关资源
    最近更新 更多