package com.jpexs.decompiler.flash.flv; import java.io.IOException; import java.io.OutputStream; import java.util.List; /** * * @author JPEXS */ public class FLVOutputStream extends OutputStream { private OutputStream os; private int bitPos = 0; private int tempByte = 0; private long pos = 0; public FLVOutputStream(OutputStream os) { this.os = os; } public long getPos() { return pos; } /** * Writes byte to the stream * * @param b byte to write * @throws IOException */ @Override public void write(int b) throws IOException { alignByte(); os.write(b); pos++; } private void alignByte() throws IOException { if (bitPos > 0) { bitPos = 0; write(tempByte); tempByte = 0; } } /** * Writes UI8 (Unsigned 8bit integer) value to the stream * * @param val UI8 value to write * @throws IOException */ public void writeUI8(int val) throws IOException { write(val); } /** * Writes UI24 (Unsigned 24bit integer) value to the stream * * @param value UI32 value * @throws IOException */ public void writeUI24(long value) throws IOException { write((int) ((value >> 16) & 0xff)); write((int) ((value >> 8) & 0xff)); write((int) (value & 0xff)); } /** * Writes UI32 (Unsigned 32bit integer) value to the stream * * @param value UI32 value * @throws IOException */ public void writeUI32(long value) throws IOException { write((int) ((value >> 24) & 0xff)); write((int) ((value >> 16) & 0xff)); write((int) ((value >> 8) & 0xff)); write((int) (value & 0xff)); } /** * Writes UI16 (Unsigned 16bit integer) value to the stream * * @param value UI16 value * @throws IOException */ public void writeUI16(int value) throws IOException { write((int) ((value >> 8) & 0xff)); write((int) (value & 0xff)); } /** * Writes UB[nBits] (Unsigned-bit value) value to the stream * * @param nBits Number of bits which represent value * @param value Unsigned value to write * @throws IOException */ public void writeUB(int nBits, long value) throws IOException { for (int bit = 0; bit < nBits; bit++) { int nb = (int) ((value >> (nBits - 1 - bit)) & 1); tempByte += nb * (1 << (7 - bitPos)); bitPos++; if (bitPos == 8) { bitPos = 0; write(tempByte); tempByte = 0; } } } public void writeHeader(boolean audio, boolean video) throws IOException { write("FLV".getBytes()); write(1); //version writeUB(5, 0); //must be 0 writeUB(1, audio ? 1 : 0); //audio present writeUB(1, 0); //reserved writeUB(1, video ? 1 : 0); //video present writeUI32(9); //header size writeUI32(0); } public void writeTag(FLVTAG tag) throws IOException { long posBefore = getPos(); writeUI8(tag.tagType); byte data[] = tag.data.getBytes(); writeUI24(data.length); writeUI24(tag.timeStamp & 0xffffff); writeUI8((int) ((tag.timeStamp >> 24) & 0xff)); writeUI24(0); write(data); //codecId 4, frameType 1 long posAfter = getPos(); long size = posAfter - posBefore; writeUI32(size); } public void writeSCRIPTDATASTRING(String s) throws IOException { int len = s.getBytes("UTF-8").length; writeUI16(len); write(s.getBytes("UTF-8")); } public void writeSCRIPTDATALONGSTRING(String s) throws IOException { int len = s.getBytes("UTF-8").length; writeUI32(len); write(s.getBytes("UTF-8")); } private void writeLong(long value) throws IOException { byte writeBuffer[] = new byte[8]; writeBuffer[3] = (byte) (value >>> 56); writeBuffer[2] = (byte) (value >>> 48); writeBuffer[1] = (byte) (value >>> 40); writeBuffer[0] = (byte) (value >>> 32); writeBuffer[7] = (byte) (value >>> 24); writeBuffer[6] = (byte) (value >>> 16); writeBuffer[5] = (byte) (value >>> 8); writeBuffer[4] = (byte) (value); write(writeBuffer); } public void writeDOUBLE(double value) throws IOException { writeLong(Double.doubleToLongBits(value)); } public void writeSCRIPTDATAOBJECT(SCRIPTDATAOBJECT o) throws IOException { writeSCRIPTDATASTRING(o.objectName); writeSCRIPTDATAVALUE(o.objectData); } public void writeSCRIPTDATAVARIABLE(SCRIPTDATAVARIABLE v) throws IOException { writeSCRIPTDATASTRING(v.variableName); writeSCRIPTDATAVALUE(v.variableData); } public void writeSCRIPTDATAVALUE(SCRIPTDATAVALUE v) throws IOException { writeUI8(v.type); switch (v.type) { case 0: writeDOUBLE((double) (Double) v.value); break; case 1: writeUI8((boolean) (Boolean) v.value ? 1 : 0); break; case 2: writeSCRIPTDATASTRING((String) v.value); break; case 3: List objects = (List) v.value; for (SCRIPTDATAOBJECT o : objects) { writeSCRIPTDATAOBJECT(o); } writeUI24(9);//SCRIPTDATAOBJECTEND break; case 4: writeSCRIPTDATASTRING((String) v.value); break; case 5: //null break; case 6: //undefined break; case 7: writeUI16((int) (Integer) v.value); break; case 8: List variables = (List) v.value; writeUI32(variables.size()); for (SCRIPTDATAVARIABLE var : variables) { writeSCRIPTDATAVARIABLE(var); } writeUI24(9);//SCRIPTDATAVARIABLEEND break; case 9: //reserved break; case 10: List stvariables = (List) v.value; writeUI32(stvariables.size()); for (SCRIPTDATAVARIABLE var : stvariables) { writeSCRIPTDATAVARIABLE(var); } break; case 11: writeSCRIPTDATADATE((SCRIPTDATADATE) v.value); break; case 12: writeSCRIPTDATALONGSTRING((String) v.value); break; } } public void writeSI16(int value) throws IOException { writeUI16(value); } public void writeSCRIPTDATADATE(SCRIPTDATADATE d) throws IOException { writeDOUBLE(d.dateTime); writeSI16(d.localDateTimeOffset); } }