Optimize RTMP read thread off

Signed-off-by: Leo Ma <begeekmyfriend@gmail.com>
camera2
Leo Ma 9 years ago
parent 7e8e23387a
commit 53132193c7

@ -21,7 +21,7 @@ Feature
Test Test
---- ----
You may watch the live broadcasting at [srs.net](http://www.ossrs.net/players/srs_player.html). You may build your own private RTMP server [srs](https://github.com/ossrs/srs/tree/2.0release).
Remember to modify the URL by yourself. Have fun! Remember to modify the URL by yourself. Have fun!
**NOTE** if you feel high latency, please check your bandwidth limits and player buffering. **NOTE** if you feel high latency, please check your bandwidth limits and player buffering.
@ -40,3 +40,8 @@ Thanks
- [MagicCamera](https://github.com/begeekmyfriend/MagicCamera) - [MagicCamera](https://github.com/begeekmyfriend/MagicCamera)
- [x264](http://www.videolan.org/developers/x264.html) - [x264](http://www.videolan.org/developers/x264.html)
- [mp4parser](https://android.googlesource.com/platform/external/mp4parser) - [mp4parser](https://android.googlesource.com/platform/external/mp4parser)
Sponsor
-------
- [dotEngine](https://dot.cc) -- A brilliant realtime multimedia communication service vendor with open cross-platform SDK ([github](https://github.com/dotEngine)).

@ -31,7 +31,7 @@ public class MainActivity extends Activity {
Button btnSwitchEncoder = null; Button btnSwitchEncoder = null;
private SharedPreferences sp; private SharedPreferences sp;
private String rtmpUrl = "rtmp://ossrs.net/" + getRandomAlphaString(3) + '/' + getRandomAlphaDigitString(5); private String rtmpUrl = "rtmp://0.0.0.0/" + getRandomAlphaString(3) + '/' + getRandomAlphaDigitString(5);
private String recPath = Environment.getExternalStorageDirectory().getPath() + "/test.mp4"; private String recPath = Environment.getExternalStorageDirectory().getPath() + "/test.mp4";
private SrsPublisher mPublisher; private SrsPublisher mPublisher;

@ -38,8 +38,6 @@ public class SrsEncoder {
public static int aChannelConfig = AudioFormat.CHANNEL_IN_STEREO; public static int aChannelConfig = AudioFormat.CHANNEL_IN_STEREO;
public static final int ABITRATE = 32 * 1000; // 32kbps public static final int ABITRATE = 32 * 1000; // 32kbps
private EventHandler mHandler;
private boolean onNetworkWeakTriggered = false;
private int mOrientation = Configuration.ORIENTATION_PORTRAIT; private int mOrientation = Configuration.ORIENTATION_PORTRAIT;
private SrsFlvMuxer flvMuxer; private SrsFlvMuxer flvMuxer;
@ -51,6 +49,8 @@ public class SrsEncoder {
private MediaCodec.BufferInfo vebi = new MediaCodec.BufferInfo(); private MediaCodec.BufferInfo vebi = new MediaCodec.BufferInfo();
private MediaCodec.BufferInfo aebi = new MediaCodec.BufferInfo(); private MediaCodec.BufferInfo aebi = new MediaCodec.BufferInfo();
private EventHandler mHandler;
private boolean networkWeakTriggered = false;
private boolean mCameraFaceFront = true; private boolean mCameraFaceFront = true;
private boolean useSoftEncoder = false; private boolean useSoftEncoder = false;
@ -63,7 +63,7 @@ public class SrsEncoder {
private int audioFlvTrack; private int audioFlvTrack;
private int audioMp4Track; private int audioMp4Track;
interface EventHandler { public interface EventHandler {
void onNetworkResume(String msg); void onNetworkResume(String msg);
@ -393,13 +393,13 @@ public class SrsEncoder {
} }
} }
if (onNetworkWeakTriggered) { if (networkWeakTriggered) {
onNetworkWeakTriggered = false; networkWeakTriggered = false;
mHandler.onNetworkResume("Network resume"); mHandler.onNetworkResume("Network resume");
} }
} else { } else {
mHandler.onNetworkWeak("Network weak"); mHandler.onNetworkWeak("Network weak");
onNetworkWeakTriggered = true; networkWeakTriggered = true;
} }
} }
@ -481,13 +481,13 @@ public class SrsEncoder {
} }
} }
if (onNetworkWeakTriggered) { if (networkWeakTriggered) {
onNetworkWeakTriggered = false; networkWeakTriggered = false;
mHandler.onNetworkResume("Network resume"); mHandler.onNetworkResume("Network resume");
} }
} else { } else {
mHandler.onNetworkWeak("Network weak"); mHandler.onNetworkWeak("Network weak");
onNetworkWeakTriggered = true; networkWeakTriggered = true;
} }
} }

@ -10,7 +10,7 @@ import java.util.ArrayList;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import net.ossrs.yasea.rtmp.RtmpPublisher; import net.ossrs.yasea.rtmp.DefaultRtmpPublisher;
/** /**
* Created by winlin on 5/2/15. * Created by winlin on 5/2/15.
@ -46,7 +46,7 @@ import net.ossrs.yasea.rtmp.RtmpPublisher;
*/ */
public class SrsFlvMuxer { public class SrsFlvMuxer {
private volatile boolean connected = false; private volatile boolean connected = false;
private SrsRtmpPublisher publisher; private DefaultRtmpPublisher publisher;
private Thread worker; private Thread worker;
private final Object txFrameLock = new Object(); private final Object txFrameLock = new Object();
@ -66,8 +66,8 @@ public class SrsFlvMuxer {
* constructor. * constructor.
* @param handler the rtmp event handler. * @param handler the rtmp event handler.
*/ */
public SrsFlvMuxer(RtmpPublisher.EventHandler handler) { public SrsFlvMuxer(DefaultRtmpPublisher.EventHandler handler) {
publisher = new SrsRtmpPublisher(handler); publisher = new DefaultRtmpPublisher(handler);
} }
/** /**

@ -112,7 +112,7 @@ public class SrsMp4Muxer {
/** /**
* MP4 recording event handler. * MP4 recording event handler.
*/ */
interface EventHandler { public interface EventHandler {
void onRecordPause(String msg); void onRecordPause(String msg);

@ -35,7 +35,6 @@ public class SrsPublisher {
public SrsPublisher(SrsCameraView view) { public SrsPublisher(SrsCameraView view) {
mCameraView = view; mCameraView = view;
mEncoder = new SrsEncoder();
mCameraView.setPreviewResolution(mEncoder.getPreviewWidth(), mEncoder.getPreviewHeight()); mCameraView.setPreviewResolution(mEncoder.getPreviewWidth(), mEncoder.getPreviewHeight());
mCameraView.setPreviewCallback(new SrsCameraView.PreviewCallback() { mCameraView.setPreviewCallback(new SrsCameraView.PreviewCallback() {
@Override @Override
@ -163,9 +162,11 @@ public class SrsPublisher {
public int getPreviewWidth() { public int getPreviewWidth() {
return mEncoder.getPreviewWidth(); return mEncoder.getPreviewWidth();
} }
public int getPreviewHeight() { public int getPreviewHeight() {
return mEncoder.getPreviewHeight(); return mEncoder.getPreviewHeight();
} }
public double getmSamplingFps() { public double getmSamplingFps() {
return mSamplingFps; return mSamplingFps;
} }

@ -1,9 +1,8 @@
package net.ossrs.yasea; package net.ossrs.yasea.rtmp;
import java.io.IOException; import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import net.ossrs.yasea.rtmp.RtmpPublisher;
import net.ossrs.yasea.rtmp.io.RtmpConnection; import net.ossrs.yasea.rtmp.io.RtmpConnection;
/** /**
@ -11,11 +10,11 @@ import net.ossrs.yasea.rtmp.io.RtmpConnection;
* *
* @author francois, leoma * @author francois, leoma
*/ */
public class SrsRtmpPublisher implements RtmpPublisher { public class DefaultRtmpPublisher implements RtmpPublisher {
private RtmpConnection rtmpConnection; private RtmpConnection rtmpConnection;
public SrsRtmpPublisher(RtmpPublisher.EventHandler handler) { public DefaultRtmpPublisher(RtmpPublisher.EventHandler handler) {
rtmpConnection = new RtmpConnection(handler); rtmpConnection = new RtmpConnection(handler);
} }

@ -1,64 +0,0 @@
package net.ossrs.yasea.rtmp.io;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketException;
import android.util.Log;
import net.ossrs.yasea.rtmp.packets.RtmpPacket;
/**
* RTMPConnection's read thread
*
* @author francois, leo
*/
public class ReadThread extends Thread {
private static final String TAG = "ReadThread";
private RtmpDecoder rtmpDecoder;
private InputStream in;
private RtmpConnection.PacketRxHandler packetRxHandler;
public ReadThread(RtmpSessionInfo rtmpSessionInfo, InputStream in, RtmpConnection.PacketRxHandler packetRxHandler) {
super("RtmpReadThread");
this.in = in;
this.packetRxHandler = packetRxHandler;
this.rtmpDecoder = new RtmpDecoder(rtmpSessionInfo);
}
@Override
public void run() {
while (!Thread.interrupted()) {
try {
// It will be blocked when no data in input stream buffer
RtmpPacket rtmpPacket = rtmpDecoder.readPacket(in);
packetRxHandler.handleRxPacket(rtmpPacket);
} catch (EOFException eof) {
this.interrupt();
// } catch (WindowAckRequired war) {
// Log.i(TAG, "Window Acknowledgment required, notifying packet handler...");
// packetRxHandler.notifyWindowAckRequired(war.getBytesRead());
// if (war.getRtmpPacket() != null) {
// // Pass to handler
// packetRxHandler.handleRxPacket(war.getRtmpPacket());
// }
} catch (SocketException se) {
Log.e(TAG, "ReadThread: Caught SocketException while reading/decoding packet, shutting down: " + se.getMessage());
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(this, se);
} catch (IOException ioe) {
Log.e(TAG, "ReadThread: Caught exception while reading/decoding packet, shutting down: " + ioe.getMessage());
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(this, ioe);
}
}
Log.i(TAG, "exit");
}
public void shutdown() {
Log.d(TAG, "Stopping");
}
}

@ -2,6 +2,7 @@ package net.ossrs.yasea.rtmp.io;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.EOFException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
@ -30,6 +31,7 @@ import net.ossrs.yasea.rtmp.packets.Data;
import net.ossrs.yasea.rtmp.packets.Handshake; import net.ossrs.yasea.rtmp.packets.Handshake;
import net.ossrs.yasea.rtmp.packets.Command; import net.ossrs.yasea.rtmp.packets.Command;
import net.ossrs.yasea.rtmp.packets.Audio; import net.ossrs.yasea.rtmp.packets.Audio;
import net.ossrs.yasea.rtmp.packets.SetPeerBandwidth;
import net.ossrs.yasea.rtmp.packets.Video; import net.ossrs.yasea.rtmp.packets.Video;
import net.ossrs.yasea.rtmp.packets.UserControl; import net.ossrs.yasea.rtmp.packets.UserControl;
import net.ossrs.yasea.rtmp.packets.RtmpPacket; import net.ossrs.yasea.rtmp.packets.RtmpPacket;
@ -46,24 +48,6 @@ public class RtmpConnection implements RtmpPublisher {
private static final Pattern rtmpUrlPattern = Pattern.compile("^rtmp://([^/:]+)(:(\\d+))*/([^/]+)(/(.*))*$"); private static final Pattern rtmpUrlPattern = Pattern.compile("^rtmp://([^/:]+)(:(\\d+))*/([^/]+)(/(.*))*$");
private RtmpPublisher.EventHandler mHandler; private RtmpPublisher.EventHandler mHandler;
private PacketRxHandler rxHandler = new PacketRxHandler() {
@Override
public void handleRxPacket(RtmpPacket rtmpPacket) {
if (rtmpPacket != null) {
rxPacketQueue.add(rtmpPacket);
}
synchronized (rxPacketLock) {
rxPacketLock.notify();
}
}
@Override
public void notifyWindowAckRequired(final int numBytesReadThusFar) {
Log.i(TAG, "notifyWindowAckRequired() called");
// Create and send window bytes read acknowledgement
sendRtmpPacket(new Acknowledgement(numBytesReadThusFar));
}
};
private String appName; private String appName;
private String streamName; private String streamName;
private String publishType; private String publishType;
@ -71,20 +55,18 @@ public class RtmpConnection implements RtmpPublisher {
private String tcUrl; private String tcUrl;
private String pageUrl; private String pageUrl;
private Socket socket; private Socket socket;
private RtmpSessionInfo rtmpSessionInfo; private RtmpSessionInfo rtmpSessionInfo = new RtmpSessionInfo();
private RtmpDecoder rtmpDecoder = new RtmpDecoder(rtmpSessionInfo);
private BufferedInputStream inputStream; private BufferedInputStream inputStream;
private BufferedOutputStream outputStream; private BufferedOutputStream outputStream;
private ReadThread readThread; private Thread rxPacketHandler;
private final ConcurrentLinkedQueue<RtmpPacket> rxPacketQueue = new ConcurrentLinkedQueue<>();
private final Object rxPacketLock = new Object();
private volatile boolean active = false;
private volatile boolean connecting = false; private volatile boolean connecting = false;
private volatile boolean fullyConnected = false; private volatile boolean fullyConnected = false;
private volatile boolean publishPermitted = false; private volatile boolean publishPermitted = false;
private final Object connectingLock = new Object(); private final Object connectingLock = new Object();
private final Object publishLock = new Object(); private final Object publishLock = new Object();
private AtomicInteger videoFrameCacheNumber = new AtomicInteger(0); private AtomicInteger videoFrameCacheNumber = new AtomicInteger(0);
private int currentStreamId = -1; private int currentStreamId = 0;
private int transactionIdCounter = 0; private int transactionIdCounter = 0;
private AmfString serverIpAddr; private AmfString serverIpAddr;
private AmfNumber serverPid; private AmfNumber serverPid;
@ -117,8 +99,8 @@ public class RtmpConnection implements RtmpPublisher {
Matcher matcher = rtmpUrlPattern.matcher(url); Matcher matcher = rtmpUrlPattern.matcher(url);
if (matcher.matches()) { if (matcher.matches()) {
tcUrl = url.substring(0, url.lastIndexOf('/')); tcUrl = url.substring(0, url.lastIndexOf('/'));
swfUrl = ""; swfUrl = "";
pageUrl = ""; pageUrl = "";
host = matcher.group(1); host = matcher.group(1);
String portStr = matcher.group(3); String portStr = matcher.group(3);
port = portStr != null ? Integer.parseInt(portStr) : 1935; port = portStr != null ? Integer.parseInt(portStr) : 1935;
@ -137,14 +119,10 @@ public class RtmpConnection implements RtmpPublisher {
outputStream = new BufferedOutputStream(socket.getOutputStream()); outputStream = new BufferedOutputStream(socket.getOutputStream());
Log.d(TAG, "connect(): socket connection established, doing handhake..."); Log.d(TAG, "connect(): socket connection established, doing handhake...");
handshake(inputStream, outputStream); handshake(inputStream, outputStream);
active = true;
Log.d(TAG, "connect(): handshake done"); Log.d(TAG, "connect(): handshake done");
rtmpSessionInfo = new RtmpSessionInfo();
readThread = new ReadThread(rtmpSessionInfo, inputStream, rxHandler);
readThread.start();
// Start the "main" handling thread // Start the "main" handling thread
new Thread(new Runnable() { rxPacketHandler = new Thread(new Runnable() {
@Override @Override
public void run() { public void run() {
@ -155,7 +133,8 @@ public class RtmpConnection implements RtmpPublisher {
Logger.getLogger(RtmpConnection.class.getName()).log(Level.SEVERE, null, ex); Logger.getLogger(RtmpConnection.class.getName()).log(Level.SEVERE, null, ex);
} }
} }
}).start(); });
rxPacketHandler.start();
rtmpConnect(); rtmpConnect();
} }
@ -211,7 +190,7 @@ public class RtmpConnection implements RtmpPublisher {
if (!fullyConnected) { if (!fullyConnected) {
throw new IllegalStateException("Not connected to RTMP server"); throw new IllegalStateException("Not connected to RTMP server");
} }
if (currentStreamId != -1) { if (currentStreamId != 0) {
throw new IllegalStateException("Current stream object has existed"); throw new IllegalStateException("Current stream object has existed");
} }
@ -252,7 +231,7 @@ public class RtmpConnection implements RtmpPublisher {
if (!fullyConnected) { if (!fullyConnected) {
throw new IllegalStateException("Not connected to RTMP server"); throw new IllegalStateException("Not connected to RTMP server");
} }
if (currentStreamId == -1) { if (currentStreamId == 0) {
throw new IllegalStateException("No current stream object exists"); throw new IllegalStateException("No current stream object exists");
} }
@ -271,7 +250,7 @@ public class RtmpConnection implements RtmpPublisher {
if (!fullyConnected) { if (!fullyConnected) {
throw new IllegalStateException("Not connected to RTMP server"); throw new IllegalStateException("Not connected to RTMP server");
} }
if (currentStreamId == -1) { if (currentStreamId == 0) {
throw new IllegalStateException("No current stream object exists"); throw new IllegalStateException("No current stream object exists");
} }
@ -299,13 +278,13 @@ public class RtmpConnection implements RtmpPublisher {
if (!fullyConnected) { if (!fullyConnected) {
throw new IllegalStateException("Not connected to RTMP server"); throw new IllegalStateException("Not connected to RTMP server");
} }
if (currentStreamId == -1) { if (currentStreamId == 0) {
throw new IllegalStateException("No current stream object exists"); throw new IllegalStateException("No current stream object exists");
} }
if (!publishPermitted) { if (!publishPermitted) {
throw new IllegalStateException("Not get the _result(Netstream.Publish.Start)"); throw new IllegalStateException("Not get the _result(Netstream.Publish.Start)");
} }
Log.d(TAG, "closeStream(): setting current stream ID to -1"); Log.d(TAG, "closeStream(): setting current stream ID to 0");
Command closeStream = new Command("closeStream", 0); Command closeStream = new Command("closeStream", 0);
closeStream.getHeader().setChunkStreamId(ChunkStreamInfo.RTMP_STREAM_CHANNEL); closeStream.getHeader().setChunkStreamId(ChunkStreamInfo.RTMP_STREAM_CHANNEL);
closeStream.getHeader().setMessageStreamId(currentStreamId); closeStream.getHeader().setMessageStreamId(currentStreamId);
@ -316,67 +295,53 @@ public class RtmpConnection implements RtmpPublisher {
@Override @Override
public void shutdown() { public void shutdown() {
if (active) { try {
readThread.shutdown(); // It will raise EOFException in handleRxPacketThread
socket.shutdownInput();
// It will raise SocketException in sendRtmpPacket
socket.shutdownOutput();
} catch (IOException ioe) {
ioe.printStackTrace();
}
try { // shutdown rxPacketHandler
// It will invoke EOFException in read thread try {
socket.shutdownInput(); rxPacketHandler.join();
// It will invoke SocketException in write thread } catch (InterruptedException ie) {
socket.shutdownOutput(); rxPacketHandler.interrupt();
} catch (IOException ioe) { }
ioe.printStackTrace();
}
// shutdown socket as well as its input and output stream
if (socket != null) {
try { try {
readThread.join(); socket.close();
} catch (InterruptedException ie) { Log.d(TAG, "socket closed");
ie.printStackTrace(); } catch (IOException ex) {
readThread.interrupt(); Log.e(TAG, "shutdown(): failed to close socket", ex);
}
// shutdown handleRxPacketLoop
rxPacketQueue.clear();
active = false;
synchronized (rxPacketLock) {
rxPacketLock.notify();
}
// shutdown socket as well as its input and output stream
if (socket != null) {
try {
socket.close();
Log.d(TAG, "socket closed");
} catch (IOException ex) {
Log.e(TAG, "shutdown(): failed to close socket", ex);
}
} }
mHandler.onRtmpDisconnected("disconnected");
} }
mHandler.onRtmpDisconnected("disconnected");
reset(); reset();
} }
private void reset() { private void reset() {
active = false;
connecting = false; connecting = false;
fullyConnected = false; fullyConnected = false;
publishPermitted = false; publishPermitted = false;
tcUrl = null; tcUrl = null;
swfUrl = null; swfUrl = null;
pageUrl = null; pageUrl = null;
appName = null; appName = null;
streamName = null; streamName = null;
publishType = null; publishType = null;
currentStreamId = -1; currentStreamId = 0;
transactionIdCounter = 0; transactionIdCounter = 0;
videoFrameCacheNumber.set(0); videoFrameCacheNumber.set(0);
socketExceptionCause = ""; socketExceptionCause = "";
serverIpAddr = null; serverIpAddr = null;
serverPid = null; serverPid = null;
serverId = null; serverId = null;
rtmpSessionInfo = null;
} }
@Override @Override
@ -384,7 +349,7 @@ public class RtmpConnection implements RtmpPublisher {
if (!fullyConnected) { if (!fullyConnected) {
throw new IllegalStateException("Not connected to RTMP server"); throw new IllegalStateException("Not connected to RTMP server");
} }
if (currentStreamId == -1) { if (currentStreamId == 0) {
throw new IllegalStateException("No current stream object exists"); throw new IllegalStateException("No current stream object exists");
} }
if (!publishPermitted) { if (!publishPermitted) {
@ -403,7 +368,7 @@ public class RtmpConnection implements RtmpPublisher {
if (!fullyConnected) { if (!fullyConnected) {
throw new IllegalStateException("Not connected to RTMP server"); throw new IllegalStateException("Not connected to RTMP server");
} }
if (currentStreamId == -1) { if (currentStreamId == 0) {
throw new IllegalStateException("No current stream object exists"); throw new IllegalStateException("No current stream object exists");
} }
if (!publishPermitted) { if (!publishPermitted) {
@ -419,7 +384,9 @@ public class RtmpConnection implements RtmpPublisher {
mHandler.onRtmpVideoStreaming("video streaming"); mHandler.onRtmpVideoStreaming("video streaming");
} }
/** Transmit the specified RTMP packet */ /**
* Transmit the specified RTMP packet
*/
public void sendRtmpPacket(RtmpPacket rtmpPacket) { public void sendRtmpPacket(RtmpPacket rtmpPacket) {
try { try {
ChunkStreamInfo chunkStreamInfo = rtmpSessionInfo.getChunkStreamInfo(rtmpPacket.getHeader().getChunkStreamId()); ChunkStreamInfo chunkStreamInfo = rtmpSessionInfo.getChunkStreamInfo(rtmpPacket.getHeader().getChunkStreamId());
@ -458,66 +425,72 @@ public class RtmpConnection implements RtmpPublisher {
} }
} }
public interface PacketRxHandler {
public void handleRxPacket(RtmpPacket rtmpPacket);
public void notifyWindowAckRequired(final int numBytesReadThusFar);
}
private void handleRxPacketLoop() throws IOException { private void handleRxPacketLoop() throws IOException {
// Handle all queued received RTMP packets // Handle all queued received RTMP packets
while (active) { while (!Thread.interrupted()) {
while (!rxPacketQueue.isEmpty()) { try {
RtmpPacket rtmpPacket = rxPacketQueue.poll(); // It will be blocked when no data in input stream buffer
//Log.d(TAG, "handleRxPacketLoop(): RTMP rx packet message type: " + rtmpPacket.getHeader().getMessageType()); RtmpPacket rtmpPacket = rtmpDecoder.readPacket(inputStream);
switch (rtmpPacket.getHeader().getMessageType()) { if (rtmpPacket != null) {
case ABORT: //Log.d(TAG, "handleRxPacketLoop(): RTMP rx packet message type: " + rtmpPacket.getHeader().getMessageType());
rtmpSessionInfo.getChunkStreamInfo(((Abort) rtmpPacket).getChunkStreamId()).clearStoredChunks(); switch (rtmpPacket.getHeader().getMessageType()) {
break; case ABORT:
case USER_CONTROL_MESSAGE: rtmpSessionInfo.getChunkStreamInfo(((Abort) rtmpPacket).getChunkStreamId()).clearStoredChunks();
UserControl ping = (UserControl) rtmpPacket; break;
switch (ping.getType()) { case USER_CONTROL_MESSAGE:
case PING_REQUEST: UserControl user = (UserControl) rtmpPacket;
ChunkStreamInfo channelInfo = rtmpSessionInfo.getChunkStreamInfo(ChunkStreamInfo.RTMP_CONTROL_CHANNEL); switch (user.getType()) {
Log.d(TAG, "handleRxPacketLoop(): Sending PONG reply.."); case STREAM_BEGIN:
UserControl pong = new UserControl(ping, channelInfo); if (currentStreamId != user.getFirstEventData()) {
sendRtmpPacket(pong); throw new IllegalStateException("Current stream ID error!");
break; }
case STREAM_EOF: break;
Log.i(TAG, "handleRxPacketLoop(): Stream EOF reached, closing RTMP writer..."); case PING_REQUEST:
break; ChunkStreamInfo channelInfo = rtmpSessionInfo.getChunkStreamInfo(ChunkStreamInfo.RTMP_CONTROL_CHANNEL);
} Log.d(TAG, "handleRxPacketLoop(): Sending PONG reply..");
break; UserControl pong = new UserControl(user, channelInfo);
case WINDOW_ACKNOWLEDGEMENT_SIZE: sendRtmpPacket(pong);
WindowAckSize windowAckSize = (WindowAckSize) rtmpPacket; break;
int size = windowAckSize.getAcknowledgementWindowSize(); case STREAM_EOF:
Log.d(TAG, "handleRxPacketLoop(): Setting acknowledgement window size: " + size); Log.i(TAG, "handleRxPacketLoop(): Stream EOF reached, closing RTMP writer...");
rtmpSessionInfo.setAcknowledgmentWindowSize(size); break;
// Set socket option default:
socket.setSendBufferSize(size); // Ignore...
break; break;
case SET_PEER_BANDWIDTH: }
int acknowledgementWindowsize = rtmpSessionInfo.getAcknowledgementWindowSize(); break;
final ChunkStreamInfo chunkStreamInfo = rtmpSessionInfo.getChunkStreamInfo(ChunkStreamInfo.RTMP_CONTROL_CHANNEL); case WINDOW_ACKNOWLEDGEMENT_SIZE:
Log.d(TAG, "handleRxPacketLoop(): Send acknowledgement window size: " + acknowledgementWindowsize); WindowAckSize windowAckSize = (WindowAckSize) rtmpPacket;
sendRtmpPacket(new WindowAckSize(acknowledgementWindowsize, chunkStreamInfo)); int size = windowAckSize.getAcknowledgementWindowSize();
break; Log.d(TAG, "handleRxPacketLoop(): Setting acknowledgement window size: " + size);
case COMMAND_AMF0: rtmpSessionInfo.setAcknowledgmentWindowSize(size);
handleRxInvoke((Command) rtmpPacket); break;
break; case SET_PEER_BANDWIDTH:
default: SetPeerBandwidth bw = (SetPeerBandwidth) rtmpPacket;
Log.w(TAG, "handleRxPacketLoop(): Not handling unimplemented/unknown packet of type: " + rtmpPacket.getHeader().getMessageType()); rtmpSessionInfo.setAcknowledgmentWindowSize(bw.getAcknowledgementWindowSize());
break; int acknowledgementWindowsize = rtmpSessionInfo.getAcknowledgementWindowSize();
} ChunkStreamInfo chunkStreamInfo = rtmpSessionInfo.getChunkStreamInfo(ChunkStreamInfo.RTMP_CONTROL_CHANNEL);
} Log.d(TAG, "handleRxPacketLoop(): Send acknowledgement window size: " + acknowledgementWindowsize);
// Wait for next received packet sendRtmpPacket(new WindowAckSize(acknowledgementWindowsize, chunkStreamInfo));
synchronized (rxPacketLock) { // Set socket option
try { socket.setSendBufferSize(acknowledgementWindowsize);
rxPacketLock.wait(500); break;
} catch (InterruptedException ex) { case COMMAND_AMF0:
Log.w(TAG, "handleRxPacketLoop: Interrupted", ex); handleRxInvoke((Command) rtmpPacket);
break;
default:
Log.w(TAG, "handleRxPacketLoop(): Not handling unimplemented/unknown packet of type: " + rtmpPacket.getHeader().getMessageType());
break;
}
} }
} catch (EOFException eof) {
Thread.currentThread().interrupt();
} catch (SocketException se) {
Log.e(TAG, "Caught SocketException while reading/decoding packet, shutting down: " + se.getMessage());
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), se);
} catch (IOException ioe) {
Log.e(TAG, "Caught exception while reading/decoding packet, shutting down: " + ioe.getMessage());
Thread.getDefaultUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), ioe);
} }
} }
} }

@ -19,7 +19,6 @@ import net.ossrs.yasea.rtmp.packets.WindowAckSize;
import net.ossrs.yasea.rtmp.packets.Acknowledgement; import net.ossrs.yasea.rtmp.packets.Acknowledgement;
/** /**
*
* @author francois * @author francois
*/ */
public class RtmpDecoder { public class RtmpDecoder {
@ -35,29 +34,25 @@ public class RtmpDecoder {
public RtmpPacket readPacket(InputStream in) throws IOException { public RtmpPacket readPacket(InputStream in) throws IOException {
RtmpHeader header = RtmpHeader.readHeader(in, rtmpSessionInfo); RtmpHeader header = RtmpHeader.readHeader(in, rtmpSessionInfo);
RtmpPacket rtmpPacket; // Log.d(TAG, "readPacket(): header.messageType: " + header.getMessageType());
Log.d(TAG, "readPacket(): header.messageType: " + header.getMessageType());
ChunkStreamInfo chunkStreamInfo = rtmpSessionInfo.getChunkStreamInfo(header.getChunkStreamId()); ChunkStreamInfo chunkStreamInfo = rtmpSessionInfo.getChunkStreamInfo(header.getChunkStreamId());
chunkStreamInfo.setPrevHeaderRx(header); chunkStreamInfo.setPrevHeaderRx(header);
if (header.getPacketLength() > rtmpSessionInfo.getRxChunkSize()) { if (header.getPacketLength() > rtmpSessionInfo.getRxChunkSize()) {
//Log.d(TAG, "readPacket(): packet size (" + header.getPacketLength() + ") is bigger than chunk size (" + rtmpSessionInfo.getChunkSize() + "); storing chunk data"); // If the packet consists of more than one chunk,
// This packet consists of more than one chunk; store the chunks in the chunk stream until everything is read // store the chunks in the chunk stream until everything is read
if (!chunkStreamInfo.storePacketChunk(in, rtmpSessionInfo.getRxChunkSize())) { if (!chunkStreamInfo.storePacketChunk(in, rtmpSessionInfo.getRxChunkSize())) {
Log.d(TAG, " readPacket(): returning null because of incomplete packet"); // return null because of incomplete packet
return null; // packet is not yet complete return null;
} else { } else {
Log.d(TAG, " readPacket(): stored chunks complete packet; reading packet"); // stored chunks complete packet, get the input stream of the chunk stream
in = chunkStreamInfo.getStoredPacketInputStream(); in = chunkStreamInfo.getStoredPacketInputStream();
} }
} else {
//Log.d(TAG, "readPacket(): packet size (" + header.getPacketLength() + ") is LESS than chunk size (" + rtmpSessionInfo.getChunkSize() + "); reading packet fully");
} }
RtmpPacket rtmpPacket;
switch (header.getMessageType()) { switch (header.getMessageType()) {
case SET_CHUNK_SIZE: case SET_CHUNK_SIZE:
SetChunkSize setChunkSize = new SetChunkSize(header); SetChunkSize setChunkSize = new SetChunkSize(header);
setChunkSize.readBody(in); setChunkSize.readBody(in);

Loading…
Cancel
Save