有点晚了,但我认为这是一个很好的问题,但还没有很好的答案。
如果您想从 Android 设备流式传输摄像头和麦克风,您有两种主要选择:Java 或 NDK 实现。
- Java 实现。
我只想提一下这个想法,但基本上它是基于这些标准Real-Time Streaming Protocol Version 2.0 和RTP Payload Format for H.264 Video 在java 中实现一个RTSP 服务器和RTP 协议。这项任务将非常漫长而艰巨。但是如果你正在做你的 PhP,那么有一个不错的 Android RTSP Java 库可能会很好。
- NDK 实施。
这是替代方案,包括各种解决方案。主要思想是在我们的 Android 应用程序中使用强大的 C 或 C++ 库。例如,FFmpeg。该库可以针对 Android 进行编译,并且可以支持各种架构。
这种方法的问题是您可能需要了解 Android NDK、C 和 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);
这部分代码展示了如何初始化名为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。
祝你好运