package com.laifeng.sopcastsdk.stream.packer.flv; import android.annotation.TargetApi; import android.media.MediaCodec; import com.laifeng.sopcastsdk.stream.packer.AnnexbHelper; import com.laifeng.sopcastsdk.stream.packer.Packer; import java.nio.ByteBuffer; import static com.laifeng.sopcastsdk.stream.packer.flv.FlvPackerHelper.AUDIO_HEADER_SIZE; import static com.laifeng.sopcastsdk.stream.packer.flv.FlvPackerHelper.AUDIO_SPECIFIC_CONFIG_SIZE; import static com.laifeng.sopcastsdk.stream.packer.flv.FlvPackerHelper.FLV_HEAD_SIZE; import static com.laifeng.sopcastsdk.stream.packer.flv.FlvPackerHelper.FLV_TAG_HEADER_SIZE; import static com.laifeng.sopcastsdk.stream.packer.flv.FlvPackerHelper.PRE_SIZE; import static com.laifeng.sopcastsdk.stream.packer.flv.FlvPackerHelper.VIDEO_HEADER_SIZE; import static com.laifeng.sopcastsdk.stream.packer.flv.FlvPackerHelper.VIDEO_SPECIFIC_CONFIG_EXTEND_SIZE; /** * @Title: FlvPacker * @Package com.laifeng.sopcastsdk.stream.packer * @Description: * @Author Jim * @Date 16/9/13 * @Time 上午11:51 * @Version */ @TargetApi(18) public class FlvPacker implements Packer, AnnexbHelper.AnnexbNaluListener{ public static final int HEADER = 0; public static final int METADATA = 1; public static final int FIRST_VIDEO = 2; public static final int FIRST_AUDIO = 3; public static final int AUDIO = 4; public static final int KEY_FRAME = 5; public static final int INTER_FRAME = 6; private OnPacketListener packetListener; private boolean isHeaderWrite; private boolean isKeyFrameWrite; private long mStartTime; private int mVideoWidth, mVideoHeight, mVideoFps; private int mAudioSampleRate, mAudioSampleSize; private boolean mIsStereo; private AnnexbHelper mAnnexbHelper; public FlvPacker() { mAnnexbHelper = new AnnexbHelper(); } public void setPacketListener(OnPacketListener listener) { packetListener = listener; } @Override public void start() { mAnnexbHelper.setAnnexbNaluListener(this); } @Override public void onVideoData(ByteBuffer bb, MediaCodec.BufferInfo bi) { mAnnexbHelper.analyseVideoData(bb, bi); } @Override public void onAudioData(ByteBuffer bb, MediaCodec.BufferInfo bi) { if(packetListener == null || !isHeaderWrite || !isKeyFrameWrite) { return; } bb.position(bi.offset); bb.limit(bi.offset + bi.size); byte[] audio = new byte[bi.size]; bb.get(audio); int compositionTime = (int) (System.currentTimeMillis() - mStartTime); int audioPacketSize = AUDIO_HEADER_SIZE + audio.length; int dataSize = audioPacketSize + FLV_TAG_HEADER_SIZE; int size = dataSize + PRE_SIZE; ByteBuffer buffer = ByteBuffer.allocate(size); FlvPackerHelper.writeFlvTagHeader(buffer, FlvPackerHelper.FlvTag.Audio, audioPacketSize, compositionTime); FlvPackerHelper.writeAudioTag(buffer, audio, false, mAudioSampleSize); buffer.putInt(dataSize); packetListener.onPacket(buffer.array(), AUDIO); } @Override public void stop() { isHeaderWrite = false; isKeyFrameWrite = false; mAnnexbHelper.stop(); } @Override public void onVideo(byte[] video, boolean isKeyFrame) { if(packetListener == null || !isHeaderWrite) { return; } int compositionTime = (int) (System.currentTimeMillis() - mStartTime); int packetType = INTER_FRAME; if(isKeyFrame) { isKeyFrameWrite = true; packetType = KEY_FRAME; } //确保第一帧是关键帧,避免一开始出现灰色模糊界面 if(!isKeyFrameWrite) { return; } int videoPacketSize = VIDEO_HEADER_SIZE + video.length; int dataSize = videoPacketSize + FLV_TAG_HEADER_SIZE; int size = dataSize + PRE_SIZE; ByteBuffer buffer = ByteBuffer.allocate(size); FlvPackerHelper.writeFlvTagHeader(buffer, FlvPackerHelper.FlvTag.Video, videoPacketSize, compositionTime); FlvPackerHelper.writeH264Packet(buffer, video, isKeyFrame); buffer.putInt(dataSize); packetListener.onPacket(buffer.array(), packetType); } @Override public void onSpsPps(byte[] sps, byte[] pps) { if(packetListener == null) { return; } //写入Flv header信息 writeFlvHeader(); //写入Meta 相关信息 writeMetaData(); //写入第一个视频信息 writeFirstVideoTag(sps, pps); //写入第一个音频信息 writeFirstAudioTag(); mStartTime = System.currentTimeMillis(); isHeaderWrite = true; } private void writeFlvHeader() { int size = FLV_HEAD_SIZE + PRE_SIZE; ByteBuffer headerBuffer = ByteBuffer.allocate(size); FlvPackerHelper.writeFlvHeader(headerBuffer, true, true); headerBuffer.putInt(0); packetListener.onPacket(headerBuffer.array(), HEADER); } private void writeMetaData() { byte[] metaData = FlvPackerHelper.writeFlvMetaData(mVideoWidth, mVideoHeight, mVideoFps, mAudioSampleRate, mAudioSampleSize, mIsStereo); int dataSize = metaData.length + FLV_TAG_HEADER_SIZE; int size = dataSize + PRE_SIZE; ByteBuffer buffer = ByteBuffer.allocate(size); FlvPackerHelper.writeFlvTagHeader(buffer, FlvPackerHelper.FlvTag.Script, metaData.length, 0); buffer.put(metaData); buffer.putInt(dataSize); packetListener.onPacket(buffer.array(), METADATA); } private void writeFirstVideoTag(byte[] sps, byte[] pps) { int firstPacketSize = VIDEO_HEADER_SIZE + VIDEO_SPECIFIC_CONFIG_EXTEND_SIZE + sps.length + pps.length; int dataSize = firstPacketSize + FLV_TAG_HEADER_SIZE; int size = dataSize + PRE_SIZE; ByteBuffer buffer = ByteBuffer.allocate(size); FlvPackerHelper.writeFlvTagHeader(buffer, FlvPackerHelper.FlvTag.Video, firstPacketSize, 0); FlvPackerHelper.writeFirstVideoTag(buffer, sps, pps); buffer.putInt(dataSize); packetListener.onPacket(buffer.array(), FIRST_VIDEO); } private void writeFirstAudioTag() { int firstAudioPacketSize = AUDIO_SPECIFIC_CONFIG_SIZE + AUDIO_HEADER_SIZE; int dataSize = FLV_TAG_HEADER_SIZE + firstAudioPacketSize; int size = dataSize + PRE_SIZE; ByteBuffer buffer = ByteBuffer.allocate(size); FlvPackerHelper.writeFlvTagHeader(buffer, FlvPackerHelper.FlvTag.Audio, firstAudioPacketSize, 0); FlvPackerHelper.writeFirstAudioTag(buffer, mAudioSampleRate, mIsStereo, mAudioSampleSize); buffer.putInt(dataSize); packetListener.onPacket(buffer.array(), FIRST_AUDIO); } public void initAudioParams(int sampleRate, int sampleSize, boolean isStereo) { mAudioSampleRate = sampleRate; mAudioSampleSize = sampleSize; mIsStereo = isStereo; } public void initVideoParams(int width, int height, int fps) { mVideoWidth = width; mVideoHeight = height; mVideoFps = fps; } }