package com.laifeng.sopcastsdk.stream.amf;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @Title: AmfObject
* @Package com.jimfengfly.rtmppublisher.amf
* @Description:
* @Author Jim
* @Date 2016/11/28
* @Time 下午1:58
* @Version
*/
public class AmfObject implements AmfData {
protected Map<String, AmfData> properties = new LinkedHashMap<>();
protected int size = -1;
/** Byte sequence that marks the end of an AMF object */
protected static final byte[] OBJECT_END_MARKER = new byte[]{0x00, 0x00, 0x09};
public AmfData getProperty(String key) {
return properties.get(key);
}
public void setProperty(String key, AmfData value) {
properties.put(key, value);
}
public void setProperty(String key, boolean value) {
properties.put(key, new AmfBoolean(value));
}
public void setProperty(String key, String value) {
properties.put(key, new AmfString(value, false));
}
public void setProperty(String key, int value) {
properties.put(key, new AmfNumber(value));
}
public void setProperty(String key, double value) {
properties.put(key, new AmfNumber(value));
}
@Override
public void writeTo(OutputStream out) throws IOException {
// Begin the object
out.write(AmfType.OBJECT.getValue());
// Write key/value pairs in this object
for (Map.Entry<String, AmfData> entry : properties.entrySet()) {
// The key must be a STRING type, and thus the "type-definition" byte is implied (not included in message)
AmfString.writeStringTo(out, entry.getKey(), true);
entry.getValue().writeTo(out);
}
// End the object
out.write(OBJECT_END_MARKER);
}
@Override
public void readFrom(InputStream in) throws IOException {
// Skip data type byte (we assume it's already read)
size = 1;
InputStream markInputStream = in.markSupported() ? in : new BufferedInputStream(in);
while (true) {
// Look for the 3-byte object end marker [0x00 0x00 0x09]
markInputStream.mark(3);
byte[] endMarker = new byte[3];
markInputStream.read(endMarker);
if (endMarker[0] == OBJECT_END_MARKER[0] && endMarker[1] == OBJECT_END_MARKER[1] && endMarker[2] == OBJECT_END_MARKER[2]) {
// End marker found
size += 3;
return;
} else {
// End marker not found; reset the stream to the marked position and read an AMF property
markInputStream.reset();
// Read the property key...
String key = AmfString.readStringFrom(in, true);
size += AmfString.sizeOf(key, true);
// ...and the property value
AmfData value = AmfDecoder.readFrom(markInputStream);
size += value.getSize();
properties.put(key, value);
}
}
}
@Override
public int getSize() {
if (size == -1) {
size = 1; // object marker
for (Map.Entry<String, AmfData> entry : properties.entrySet()) {
size += AmfString.sizeOf(entry.getKey(), true);
size += entry.getValue().getSize();
}
size += 3; // end of object marker
}
return size;
}
@Override
public byte[] getBytes() {
int size = getSize();
ByteBuffer byteBuffer = ByteBuffer.allocate(size);
byteBuffer.put(AmfType.OBJECT.getValue());
// Write key/value pairs in this object
for (Map.Entry<String, AmfData> entry : properties.entrySet()) {
// The key must be a STRING type, and thus the "type-definition" byte is implied (not included in message)
AmfString keyString = new AmfString(entry.getKey(), true);
byteBuffer.put(keyString.getBytes());
byteBuffer.put(entry.getValue().getBytes());
}
byteBuffer.put(OBJECT_END_MARKER);
return byteBuffer.array();
}
}