You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于Android现有相机预览byte[]实现局域网设备间RTSP流传输

我完全懂你的困扰——那些现成的RTSP库总是要重新初始化相机预览,直接把你已经搞定的运动检测和视频录制逻辑给绕过去了。其实咱们完全可以基于现有的预览byte[]数据来实现RTSP流传输,核心就是把原始YUV数据编码成H.264,再通过RTSP/RTP协议推出去,全程不用碰相机的重新初始化。下面是具体的实现思路和关键代码:

1. 先明确基础前提

相机返回的byte[]默认是NV21格式的YUV数据,而RTSP传输的视频流几乎都是H.264编码的,所以第一步必须完成「YUV → H.264」的编码,这里推荐用Android原生的MediaCodec硬编码,效率高且无额外依赖。

2. 初始化MediaCodec编码器

要保证编码器的参数和相机预览参数完全匹配(分辨率、帧率),否则会出现编码失败或画面异常:

// 替换成你实际的相机预览参数
private static final int PREVIEW_WIDTH = 1280;
private static final int PREVIEW_HEIGHT = 720;
private static final int FRAME_RATE = 30;
private MediaCodec h264Codec;

private void initH264Encoder() {
    try {
        // 创建H.264编码器
        h264Codec = MediaCodec.createEncoderByType("video/avc");
        MediaFormat encodeFormat = MediaFormat.createVideoFormat(
                "video/avc", PREVIEW_WIDTH, PREVIEW_HEIGHT
        );

        // 配置编码器参数
        encodeFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT,
                MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible); // 兼容NV21
        encodeFormat.setInteger(MediaFormat.KEY_BIT_RATE, 2 * 1024 * 1024); // 2Mbps码率,可按需调整
        encodeFormat.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
        encodeFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5); // 每5秒生成一个关键帧

        // 启动编码器
        h264Codec.configure(encodeFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        h264Codec.start();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

3. 在PreviewCallback中喂数据给编码器

把相机返回的byte[]直接传给编码器,同时处理编码后的H.264数据:

mPreviewCallBack = new android.hardware.Camera.PreviewCallback() {
    @Override
    public void onPreviewFrame(byte[] data, android.hardware.Camera camera) {
        // 保留你已有的运动检测和录制逻辑
        detectMotion(data);
        recordVideo(data);

        // 处理RTSP流:喂数据给编码器
        processFrameForRtsp(data);
    }
};

private void processFrameForRtsp(byte[] yuvData) {
    try {
        // 获取编码器输入缓冲区
        int inputBufferIdx = h264Codec.dequeueInputBuffer(10_000); // 10ms超时
        if (inputBufferIdx >= 0) {
            ByteBuffer inputBuffer = h264Codec.getInputBuffer(inputBufferIdx);
            inputBuffer.clear();
            inputBuffer.put(yuvData);

            // 计算时间戳(微秒),保证视频时序正确
            long presentationTimeUs = System.nanoTime() / 1000;
            h264Codec.queueInputBuffer(
                    inputBufferIdx, 0, yuvData.length, presentationTimeUs, 0
            );
        }

        // 获取编码器输出的H.264数据
        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
        int outputBufferIdx = h264Codec.dequeueOutputBuffer(bufferInfo, 10_000);
        while (outputBufferIdx >= 0) {
            ByteBuffer outputBuffer = h264Codec.getOutputBuffer(outputBufferIdx);
            if (bufferInfo.size > 0) {
                // 判断是否是SPS/PPS配置帧(H.264必需的初始化数据)
                boolean isConfigFrame = (bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0;
                // 把H.264数据封装成RTP包并发送
                sendH264OverRtp(outputBuffer, bufferInfo.presentationTimeUs, isConfigFrame);
            }

            h264Codec.releaseOutputBuffer(outputBufferIdx, false);
            outputBufferIdx = h264Codec.dequeueOutputBuffer(bufferInfo, 0);
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

4. 实现RTSP/RTP传输逻辑

RTSP是控制协议(处理客户端的连接、播放请求),实际视频数据通过RTP/UDP传输。这里需要做两件事:

4.1 处理RTSP会话

你需要用ServerSocket监听RTSP端口(默认554),处理客户端发送的OPTIONS、DESCRIBE、SETUP、PLAY等请求,返回符合规范的响应,同时协商RTP传输的端口。

关键是在DESCRIBE响应中返回SDP描述,包含H.264的编码参数(SPS、PPS、profile-level-id等),这些参数可以从编码器输出的配置帧中提取。

4.2 H.264转RTP封装

H.264的NALU单元(编码后的帧数据)如果小于MTU(比如1400字节),可以直接作为RTP负载;如果大于,需要分片成FU-A包。同时RTP时间戳要基于90kHz时钟(H.264标准),转换方式如下:

// 把微秒时间戳转成90kHz的RTP时间戳
long rtpTimestamp = (presentationTimeUs * 90) / 1000;

你可以用DatagramSocket来发送RTP包,每个包需要包含RTP头部(版本、序列号、时间戳、SSRC等)和H.264负载。

5. 资源释放

在相机关闭或页面销毁时,一定要释放编码器和Socket资源:

private void releaseResources() {
    if (h264Codec != null) {
        h264Codec.stop();
        h264Codec.release();
        h264Codec = null;
    }
    // 关闭RTSP ServerSocket和RTP DatagramSocket
    // ...
}

关键注意事项

  • 确保相机预览的YUV格式和编码器颜色格式兼容,NV21一般兼容COLOR_FormatYUV420Flexible,如果不行可以尝试COLOR_FormatYUV420SemiPlanar。
  • SPS/PPS必须在视频流开始前发送给客户端,否则客户端无法解码。
  • RTP包的序列号要严格递增,SSRC要保持唯一,保证客户端能正确拼接帧数据。

内容的提问来源于stack exchange,提问作者Tran Intent

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:24:19