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

如何在Android应用中接收H264格式IP视频流并实现逐帧采集

实现Android端接收UDP H264流并逐帧采集的方案

所需核心组件

你之前查询的AudioManager是音频相关组件,和当前视频流接收场景完全无关,无需关注。核心用到的组件如下:

  • 网络层:DatagramSocket,Colasoft Packet Player默认发的是UDP协议裸流,不需要TCP握手逻辑,直接用该类监听对应端口收包即可
  • 解封装层:自研H264 NALU拼接逻辑,UDP传输时H264帧会被拆成多个分片,需要拼接成完整的I/P/B帧再送解码
  • 解码层:Android原生MediaCodec硬解码器,性能远高于软解码,直接喂入完整NALU即可输出解码后的YUV/RGB帧,满足逐帧采集需求
  • 可选渲染层:SurfaceView/TextureView,仅需要逐帧采集不需要画面展示的话可以不用

具体实现步骤

步骤1:添加必要权限

在AndroidManifest.xml中添加如下权限:

<!-- 网络操作权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 采集的帧需要存储到本地再加存储权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

步骤2:启动UDP收包线程

网络操作必须在子线程执行,禁止放到主线程,参考逻辑如下:

private DatagramSocket mUdpSocket;
private final byte[] mReceiveBuf = new byte[2048]; // 普通UDP包最大长度1500,设2048足够
private boolean mIsReceiving = false;

// 该方法需要在子线程调用
public void startReceive(int targetPort) {
    mIsReceiving = true;
    try {
        mUdpSocket = new DatagramSocket(targetPort);
        DatagramPacket packet = new DatagramPacket(mReceiveBuf, mReceiveBuf.length);
        while (mIsReceiving) {
            mUdpSocket.receive(packet);
            int dataLen = packet.getLength();
            byte[] realData = new byte[dataLen];
            System.arraycopy(mReceiveBuf, 0, realData, 0, dataLen);
            // 收到的包送入NALU拼接队列处理
            addToNaluCache(realData);
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (mUdpSocket != null) {
            mUdpSocket.close();
        }
    }
}

步骤3:拼接完整H264 NALU帧

H264帧有固定起始码,要么是0x00 0x00 0x00 0x01,要么是0x00 0x00 0x01,只需要缓存收到的数据包,检测到下一个起始码时,就把之前缓存的内容作为一个完整的NALU帧输出即可。

注意:如果你的流带RTP封装,需要先去掉12字节的RTP头再做NALU拼接,可以提前用Wireshark抓包确认包结构。

步骤4:MediaCodec解码获取逐帧数据

硬解码器初始化参考逻辑:

private MediaCodec mMediaCodec;
private boolean mIsDecoding = false;

public void initDecoder(int videoWidth, int videoHeight) {
    try {
        MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, videoWidth, videoHeight);
        mMediaCodec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
        // 第二个参数传Surface可直接渲染画面,仅需要采集帧的话传null,解码后数据会存在ByteBuffer中
        mMediaCodec.configure(format, null, null, 0);
        mMediaCodec.start();
        mIsDecoding = true;
        // 启动独立解码线程,从NALU队列取帧喂入解码器,再从输出缓冲区读取解码后的帧数据即可
        startDecodeWorker();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

注意事项

  • 收包、解码逻辑都要放在独立子线程执行,禁止阻塞主线程,避免出现ANR
  • 解码前必须先拿到SPS、PPS参数帧,一般是流开头的前两个NALU,缺少参数帧解码器无法正常工作
  • 出现花屏优先排查丢包、NALU拼接错误两个问题
  • 如果H264流带B帧,解码顺序和显示顺序不一致,需要按PTS排序后再做帧采集

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 14:15:05