首页  编辑  

安卓下录制RTSP/RTP H264流的最简单的方法

Tags: /Android/   Date Created:
The best way to record RTSP/RTP h264 stream from Webcam/IPCamera etc.

每个关键帧前面必定是SPS/PPS/SEI包,所以使用MediaMuxer前,一定要等到收到前面的SPS/PPS之后,才初始化Muxer。
RTSP/RTP包最开始的packet都是SPS/PPS,所以,在RTP包处理时:

on RTP Packet:
private MediaFormat mediaFormat = MediaFormat.createVideoFormat("video/avc", 1920, 1080);
private MediaMuxer muxer;
private MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
private int trackVideo = 0;
private boolean formatSetted = false;
private boolean videoReady = false;
private byte[] pps, pps_frame, sps, sps_frame;

private byte[] trimFrame(byte[] save) {
    int index = 0;
    int len = 0;
    byte[] buffer = new byte[save.length];
    while (index < save.length - 2) {
        if (save[index] == 0 && save[index + 1] == 0 && (save[index + 2] == 1 || save[index + 2] == 3))
            index += 3;
        else
            buffer[len++] = save[index++];
    }
    while (index < save.length)  // 补齐尾部数据
        buffer[len++] = save[index++];

    byte[] ret = new byte[len];
    System.arraycopy(buffer, 0, ret, 0, len);
    return ret;
}
private void initMuxer() {
    try {
        muxer = new MediaMuxer("/sdcard/DCIM/demo.mp4", MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
    } catch (IOException e) {
        muxer = null;
        return;
    }
    mediaFormat = MediaFormat.createVideoFormat("video/avc", 1920, 1080);
    mediaFormat.setByteBuffer("csd-0", ByteBuffer.wrap(pps));
    mediaFormat.setByteBuffer("csd-1", ByteBuffer.wrap(sps));
    mediaFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 1920 * 1080);
    mediaFormat.setInteger(MediaFormat.KEY_CAPTURE_RATE, 25);
    mediaFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
    trackVideo = muxer.addTrack(mediaFormat);

    muxer.start();

    MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
    bufferInfo.offset = 0;
    bufferInfo.flags = MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
    bufferInfo.presentationTimeUs = System.nanoTime() / 1000;

    bufferInfo.size = sps_frame.length;
    muxer.writeSampleData(trackVideo, ByteBuffer.wrap(sps_frame), bufferInfo);

    bufferInfo.size = pps_frame.length;
    muxer.writeSampleData(trackVideo, ByteBuffer.wrap(pps_frame), bufferInfo);

    videoReady = true;
}
public void onPacket(int channel, byte[] packet, int len) {
            if (codec == null) return;

            byte[] frame = rtph264.decode(packet, len);
            if (frame == null) return;

            int flag = rtph264.getType(packet);
//            Log.e(TAG, "时间序列: " + rtph264.timestamp + " 帧类型: " + flag + " 帧长度: " + frame.length);

            // 海康Flag顺序: 7,8,6
            if (flag == 8) {  // pps
                pps = trimFrame(frame);
                formatSetted = true;
                pps_frame = frame;
            } else if (flag == 7) {
                sps = trimFrame(frame);
                sps_frame = frame;
            }

            if (formatSetted && !videoReady) {
                if (muxer == null) initMuxer();
            }

            if (videoReady) {
                try {
                    MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
                    bufferInfo.offset = 0;
                    bufferInfo.size = frame.length;
                    if (flag == 8 || flag == 6 || flag == 7) {
                        bufferInfo.flags = MediaCodec.BUFFER_FLAG_CODEC_CONFIG;
                    } else if (flag == 28 || flag == 1) {
                        bufferInfo.flags = MediaCodec.BUFFER_FLAG_KEY_FRAME;
                    }

                    bufferInfo.presentationTimeUs = System.nanoTime() / 1000;
                    muxer.writeSampleData(trackVideo, ByteBuffer.wrap(frame), bufferInfo);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
}
rtph264.java,请参考 安卓Android Java中从RTP数据包中抽取完整H264帧以便解码? (cipindanci.com)