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);
if (flag == 8) {
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();
}
}
}