// Path: app/src/main/java/org/yeshen/video/librtmp/unstable/net/Frame.java
// public class Frame {
//     public static final int FRAME_TYPE_AUDIO = 1;
//     public static final int FRAME_TYPE_KEY_FRAME = 2;
//     public static final int FRAME_TYPE_INTER_FRAME = 3;
//     public static final int FRAME_TYPE_CONFIGURATION = 4;
// 
//     public Chunk data;
//     public int packetType;
//     public int frameType;
// 
//     public Frame(Chunk data, int packetType, int frameType) {
//         this.data = data;
//         this.packetType = packetType;
//         this.frameType = frameType;
//     }
// }
// 
// Path: app/src/main/java/org/yeshen/video/librtmp/unstable/net/sender/rtmp/packets/Chunk.java
// public abstract class Chunk {
//      
//     protected ChunkHeader header;
// 
//     public Chunk(ChunkHeader header) {
//         this.header = header;
//     }
// 
//     public ChunkHeader getChunkHeader() {
//         return header;
//     }
//     
//     public abstract void readBody(InputStream in) throws IOException;    
//     
//     protected abstract void writeBody(OutputStream out) throws IOException;
//            
//     public void writeTo(OutputStream out, final SessionInfo sessionInfo) throws IOException {
//         int chunkSize = sessionInfo.getTxChunkSize();
//         ByteArrayOutputStream baos = new ByteArrayOutputStream();
//         writeBody(baos);        
//         byte[] body = baos.toByteArray();        
//         header.setPacketLength(body.length);
//         // Write header for first chunk
//         header.writeTo(out, ChunkType.TYPE_0_FULL, sessionInfo);
//         int remainingBytes = body.length;
//         int pos = 0;
//         while (remainingBytes > chunkSize) {
//             // Write packet for chunk
//             out.write(body, pos, chunkSize);
//             remainingBytes -= chunkSize;
//             pos += chunkSize;
//             // Write header for remain chunk
//             header.writeTo(out, ChunkType.TYPE_3_NO_BYTE, sessionInfo);
//         }
//         out.write(body, pos, remainingBytes);
//     }
// }
// 
// Path: app/src/main/java/org/yeshen/video/librtmp/unstable/net/sender/rtmp/packets/Command.java
// public class Command extends VariableBodyRtmpPacket {
// 
//     private static final String TAG = "Command";
// 
//     private String commandName;
//     private int transactionId;    
// 
//     public Command(ChunkHeader header) {
//         super(header);
//     }
//     
//     public Command(String commandName, int transactionId) {
//         super(new ChunkHeader(ChunkType.TYPE_0_FULL, SessionInfo.RTMP_COMMAND_CHANNEL, MessageType.COMMAND_AMF0));
//         this.commandName = commandName;
//         this.transactionId = transactionId;
//     }
// 
//     public String getCommandName() {
//         return commandName;
//     }
// 
//     public void setCommandName(String commandName) {
//         this.commandName = commandName;
//     }
// 
//     public int getTransactionId() {
//         return transactionId;
//     }
// 
//     public void setTransactionId(int transactionId) {
//         this.transactionId = transactionId;
//     }    
// 
//     @Override
//     public void readBody(InputStream in) throws IOException {
//         // The command name and transaction ID are always present (AMF string followed by number)
//         commandName = AmfString.readStringFrom(in, false);
//         transactionId = (int) AmfNumber.readNumberFrom(in);
//         int bytesRead = AmfString.sizeOf(commandName, false) + AmfNumber.SIZE;
//         readVariableData(in, bytesRead);
//     }
// 
//     @Override
//     protected void writeBody(OutputStream out) throws IOException {
//         AmfString.writeStringTo(out, commandName, false);
//         AmfNumber.writeNumberTo(out, transactionId);
//         // Write body data
//         writeVariableData(out);
//     }
//     
//     @Override
//     public String toString() {
//         return "RTMP Command (command: " + commandName + ", transaction ID: " + transactionId + ")";
//     }
// }
// 
// Path: app/src/main/java/org/yeshen/video/librtmp/unstable/net/sender/sendqueue/ISendQueue.java
// public interface ISendQueue {
//     void start();
//     void stop();
//     void setBufferSize(int size);
//     void putFrame(Frame frame);
//     Frame takeFrame();
//     void setSendQueueListener(SendQueueListener listener);
// }


import org.yeshen.video.librtmp.unstable.net.Frame;
import org.yeshen.video.librtmp.unstable.net.sender.rtmp.packets.Chunk;
import org.yeshen.video.librtmp.unstable.net.sender.rtmp.packets.Command;
import org.yeshen.video.librtmp.unstable.net.sender.sendqueue.ISendQueue;
import java.io.IOException;
import java.io.OutputStream;
        this.startFlag = true;
    }

    void setWriteListener(OnWriteListener listener) {
        this.listener = listener;
    }

    @Override
    public void run() {
        while (startFlag) {
            try {
                Frame frame = mSendQueue.takeFrame();
                if(frame != null) {
                    Chunk chunk = frame.data;
                    chunk.writeTo(out, sessionInfo);
                    if (chunk instanceof Command) {
                        Command command = (Command) chunk;
                        sessionInfo.addInvokedCommand(command.getTransactionId(), command.getCommandName());
                    }
                    out.flush();
                }
            } catch (IOException e) {
                startFlag = false;
                if(listener != null) {
                    listener.onDisconnect();
                }
            }
        }
    }

// Path: app/src/main/java/org/yeshen/video/librtmp/unstable/net/Frame.java
// public class Frame {
//     public static final int FRAME_TYPE_AUDIO = 1;
//     public static final int FRAME_TYPE_KEY_FRAME = 2;
//     public static final int FRAME_TYPE_INTER_FRAME = 3;
//     public static final int FRAME_TYPE_CONFIGURATION = 4;
// 
//     public Chunk data;
//     public int packetType;
//     public int frameType;
// 
//     public Frame(Chunk data, int packetType, int frameType) {
//         this.data = data;
//         this.packetType = packetType;
//         this.frameType = frameType;
//     }
// }
// 
// Path: app/src/main/java/org/yeshen/video/librtmp/unstable/net/sender/rtmp/packets/Chunk.java
// public abstract class Chunk {
//      
//     protected ChunkHeader header;
// 
//     public Chunk(ChunkHeader header) {
//         this.header = header;
//     }
// 
//     public ChunkHeader getChunkHeader() {
//         return header;
//     }
//     
//     public abstract void readBody(InputStream in) throws IOException;    
//     
//     protected abstract void writeBody(OutputStream out) throws IOException;
//            
//     public void writeTo(OutputStream out, final SessionInfo sessionInfo) throws IOException {
//         int chunkSize = sessionInfo.getTxChunkSize();
//         ByteArrayOutputStream baos = new ByteArrayOutputStream();
//         writeBody(baos);        
//         byte[] body = baos.toByteArray();        
//         header.setPacketLength(body.length);
//         // Write header for first chunk
//         header.writeTo(out, ChunkType.TYPE_0_FULL, sessionInfo);
//         int remainingBytes = body.length;
//         int pos = 0;
//         while (remainingBytes > chunkSize) {
//             // Write packet for chunk
//             out.write(body, pos, chunkSize);
//             remainingBytes -= chunkSize;
//             pos += chunkSize;
//             // Write header for remain chunk
//             header.writeTo(out, ChunkType.TYPE_3_NO_BYTE, sessionInfo);
//         }
//         out.write(body, pos, remainingBytes);
//     }
// }
// 
// Path: app/src/main/java/org/yeshen/video/librtmp/unstable/net/sender/rtmp/packets/Command.java
// public class Command extends VariableBodyRtmpPacket {
// 
//     private static final String TAG = "Command";
// 
//     private String commandName;
//     private int transactionId;    
// 
//     public Command(ChunkHeader header) {
//         super(header);
//     }
//     
//     public Command(String commandName, int transactionId) {
//         super(new ChunkHeader(ChunkType.TYPE_0_FULL, SessionInfo.RTMP_COMMAND_CHANNEL, MessageType.COMMAND_AMF0));
//         this.commandName = commandName;
//         this.transactionId = transactionId;
//     }
// 
//     public String getCommandName() {
//         return commandName;
//     }
// 
//     public void setCommandName(String commandName) {
//         this.commandName = commandName;
//     }
// 
//     public int getTransactionId() {
//         return transactionId;
//     }
// 
//     public void setTransactionId(int transactionId) {
//         this.transactionId = transactionId;
//     }    
// 
//     @Override
//     public void readBody(InputStream in) throws IOException {
//         // The command name and transaction ID are always present (AMF string followed by number)
//         commandName = AmfString.readStringFrom(in, false);
//         transactionId = (int) AmfNumber.readNumberFrom(in);
//         int bytesRead = AmfString.sizeOf(commandName, false) + AmfNumber.SIZE;
//         readVariableData(in, bytesRead);
//     }
// 
//     @Override
//     protected void writeBody(OutputStream out) throws IOException {
//         AmfString.writeStringTo(out, commandName, false);
//         AmfNumber.writeNumberTo(out, transactionId);
//         // Write body data
//         writeVariableData(out);
//     }
//     
//     @Override
//     public String toString() {
//         return "RTMP Command (command: " + commandName + ", transaction ID: " + transactionId + ")";
//     }
// }
// 
// Path: app/src/main/java/org/yeshen/video/librtmp/unstable/net/sender/sendqueue/ISendQueue.java
// public interface ISendQueue {
//     void start();
//     void stop();
//     void setBufferSize(int size);
//     void putFrame(Frame frame);
//     Frame takeFrame();
//     void setSendQueueListener(SendQueueListener listener);
// }


// Path: app/src/main/java/org/yeshen/video/librtmp/unstable/net/sender/rtmp/io/WriteThread.java
import org.yeshen.video.librtmp.unstable.net.Frame;
import org.yeshen.video.librtmp.unstable.net.sender.rtmp.packets.Chunk;
import org.yeshen.video.librtmp.unstable.net.sender.rtmp.packets.Command;
import org.yeshen.video.librtmp.unstable.net.sender.sendqueue.ISendQueue;
import java.io.IOException;
import java.io.OutputStream;

        this.startFlag = true;
    }

    void setWriteListener(OnWriteListener listener) {
        this.listener = listener;
    }

    @Override
    public void run() {
        while (startFlag) {
            try {
                Frame frame = mSendQueue.takeFrame();
                if(frame != null) {
                    Chunk chunk = frame.data;
                    chunk.writeTo(out, sessionInfo);
                    if (chunk instanceof Command) {
                        Command command = (Command) chunk;
                        sessionInfo.addInvokedCommand(command.getTransactionId(), command.getCommandName());
                    }
                    out.flush();
                }
            } catch (IOException e) {
                startFlag = false;
                if(listener != null) {
                    listener.onDisconnect();
                }
            }
        }
    }

    void setSendQueue(ISendQueue sendQueue) {