package com.laifeng.sopcastsdk.stream.sender.rtmp.io; import android.util.Log; import com.laifeng.sopcastsdk.stream.sender.rtmp.packets.Abort; import com.laifeng.sopcastsdk.stream.sender.rtmp.packets.Acknowledgement; import com.laifeng.sopcastsdk.stream.sender.rtmp.packets.Audio; import com.laifeng.sopcastsdk.stream.sender.rtmp.packets.Command; import com.laifeng.sopcastsdk.stream.sender.rtmp.packets.Data; import com.laifeng.sopcastsdk.stream.sender.rtmp.packets.ChunkHeader; import com.laifeng.sopcastsdk.stream.sender.rtmp.packets.Chunk; import com.laifeng.sopcastsdk.stream.sender.rtmp.packets.SetChunkSize; import com.laifeng.sopcastsdk.stream.sender.rtmp.packets.SetPeerBandwidth; import com.laifeng.sopcastsdk.stream.sender.rtmp.packets.UserControl; import com.laifeng.sopcastsdk.stream.sender.rtmp.packets.Video; import com.laifeng.sopcastsdk.stream.sender.rtmp.packets.WindowAckSize; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; /** * * @author francois */ public class RtmpDecoder { private static final String TAG = "RtmpDecoder"; private SessionInfo sessionInfo; private HashMap<Integer, StoreChunk> storeChunkHashMap = new HashMap<>(); public RtmpDecoder(SessionInfo sessionInfo) { this.sessionInfo = sessionInfo; } public Chunk readPacket(InputStream in) throws IOException { ChunkHeader header = ChunkHeader.readHeader(in, sessionInfo); Chunk rtmpPacket; Log.d(TAG, "readPacket(): header.messageType: " + header.getMessageType()); int messageLength = header.getPacketLength(); if (header.getPacketLength() > sessionInfo.getRxChunkSize()) { //Log.d(TAG, "readPacket(): packet size (" + header.getPacketLength() + ") is bigger than chunk size (" + rtmpSessionInfo.getChunkSize() + "); storing chunk data"); // This packet consists of more than one chunk; store the chunks in the chunk stream until everything is read StoreChunk storeChunk = storeChunkHashMap.get(header.getChunkStreamId()); if(storeChunk == null) { storeChunk = new StoreChunk(); storeChunkHashMap.put(header.getChunkStreamId(), storeChunk); } if (!storeChunk.storeChunk(in, messageLength, sessionInfo.getRxChunkSize())) { Log.d(TAG, " readPacket(): returning null because of incomplete packet"); return null; // packet is not yet complete } else { Log.d(TAG, " readPacket(): stored chunks complete packet; reading packet"); in = storeChunk.getStoredInputStream(); } } else { //Log.d(TAG, "readPacket(): packet size (" + header.getPacketLength() + ") is LESS than chunk size (" + rtmpSessionInfo.getChunkSize() + "); reading packet fully"); } switch (header.getMessageType()) { case SET_CHUNK_SIZE: SetChunkSize setChunkSize = new SetChunkSize(header); setChunkSize.readBody(in); Log.d(TAG, "readPacket(): Setting chunk size to: " + setChunkSize.getChunkSize()); sessionInfo.setRxChunkSize(setChunkSize.getChunkSize()); return null; case ABORT: rtmpPacket = new Abort(header); break; case USER_CONTROL_MESSAGE: rtmpPacket = new UserControl(header); break; case WINDOW_ACKNOWLEDGEMENT_SIZE: rtmpPacket = new WindowAckSize(header); break; case SET_PEER_BANDWIDTH: rtmpPacket = new SetPeerBandwidth(header); break; case AUDIO: rtmpPacket = new Audio(header); break; case VIDEO: rtmpPacket = new Video(header); break; case COMMAND_AMF0: rtmpPacket = new Command(header); break; case DATA_AMF0: rtmpPacket = new Data(header); break; case ACKNOWLEDGEMENT: rtmpPacket = new Acknowledgement(header); break; default: throw new IOException("No packet body implementation for message type: " + header.getMessageType()); } rtmpPacket.readBody(in); return rtmpPacket; } public void clearStoredChunks(int chunkStreamId) { StoreChunk storeChunk = storeChunkHashMap.get(chunkStreamId); if(storeChunk != null) { storeChunk.clearStoredChunks(); } } }