use of org.red5.io.amf3.Input in project red5-io by Red5.
the class FLVReader method getDuration.
/**
* Returns the last tag's timestamp as the files duration.
*
* @param flvFile
* FLV file
* @return duration
*/
public static int getDuration(File flvFile) {
int duration = 0;
RandomAccessFile flv = null;
try {
flv = new RandomAccessFile(flvFile, "r");
long flvLength = Math.max(flvFile.length(), flv.length());
log.debug("File length: {}", flvLength);
if (flvLength > 13) {
flv.seek(flvLength - 4);
int lastTagSize = flv.readInt();
log.debug("Last tag size: {}", lastTagSize);
if (lastTagSize > 0 && (lastTagSize < flvLength)) {
// jump right to where tag timestamp would be
flv.seek(flvLength - lastTagSize);
// grab timestamp as a regular int
duration = flv.readInt();
// adjust value to match extended timestamp
duration = (duration >>> 8) | ((duration & 0x000000ff) << 24);
} else {
// attempt to read the metadata
flv.seek(13);
byte tagType = flv.readByte();
if (tagType == ITag.TYPE_METADATA) {
ByteBuffer buf = ByteBuffer.allocate(3);
flv.getChannel().read(buf);
int bodySize = IOUtils.readMediumInt(buf);
log.debug("Metadata body size: {}", bodySize);
// timestamp
flv.skipBytes(4);
// stream id
flv.skipBytes(3);
buf.clear();
buf = ByteBuffer.allocate(bodySize);
flv.getChannel().read(buf);
// construct the meta
IoBuffer ioBuf = IoBuffer.wrap(buf);
Input input = new Input(ioBuf);
String metaType = Deserializer.deserialize(input, String.class);
log.debug("Metadata type: {}", metaType);
Map<String, ?> meta = Deserializer.deserialize(input, Map.class);
Object tmp = meta.get("duration");
if (tmp != null) {
if (tmp instanceof Double) {
duration = ((Double) tmp).intValue();
} else {
duration = Integer.valueOf((String) tmp);
}
}
input = null;
meta.clear();
meta = null;
ioBuf.clear();
ioBuf.free();
ioBuf = null;
}
}
}
} catch (IOException e) {
log.warn("Exception getting file duration", e);
} finally {
try {
if (flv != null) {
flv.close();
}
} catch (IOException e) {
}
flv = null;
}
return duration;
}
use of org.red5.io.amf3.Input in project red5-server-common by Red5.
the class RTMPProtocolDecoder method decodeStreamData.
/**
* Decodes stream data, to include onMetaData, onCuePoint, and onFI.
*
* @param in
* input buffer
* @return Notify
*/
@SuppressWarnings("unchecked")
public Notify decodeStreamData(IoBuffer in) {
if (log.isDebugEnabled()) {
log.debug("decodeStreamData");
}
// our result is a notify
Notify ret = null;
// check the encoding, if its AMF3 check to see if first byte is set to AMF0
Encoding encoding = ((RTMPConnection) Red5.getConnectionLocal()).getEncoding();
log.trace("Encoding: {}", encoding);
// set mark
in.mark();
// create input using AMF0 to start with
Input input = new org.red5.io.amf.Input(in);
if (encoding == Encoding.AMF3) {
log.trace("Client indicates its using AMF3");
}
// get the first datatype
byte dataType = input.readDataType();
log.debug("Data type: {}", dataType);
if (dataType == DataTypes.CORE_STRING) {
String action = input.readString();
if ("@setDataFrame".equals(action)) {
// get the second datatype
byte dataType2 = input.readDataType();
log.debug("Dataframe method type: {}", dataType2);
String onCueOrOnMeta = input.readString();
// get the params datatype
byte object = input.readDataType();
if (object == DataTypes.CORE_SWITCH) {
log.trace("Switching decoding to AMF3");
input = new org.red5.io.amf3.Input(in);
((org.red5.io.amf3.Input) input).enforceAMF3();
// re-read data type after switching decode
object = input.readDataType();
}
log.debug("Dataframe params type: {}", object);
Map<Object, Object> params = Collections.EMPTY_MAP;
if (object == DataTypes.CORE_MAP) {
// the params are sent as a Mixed-Array. Required to support the RTMP publish provided by ffmpeg
params = (Map<Object, Object>) input.readMap();
} else if (object == DataTypes.CORE_ARRAY) {
params = (Map<Object, Object>) input.readArray(Object[].class);
} else if (object == DataTypes.CORE_STRING) {
// decode the string and drop-in as first map entry since we dont know how its encoded
String str = input.readString();
log.debug("String params: {}", str);
params = new HashMap<>();
params.put("0", str);
// } else if (object == DataTypes.CORE_OBJECT) {
// params = (Map<Object, Object>) input.readObject();
} else {
try {
// read the params as a standard object
params = (Map<Object, Object>) input.readObject();
} catch (Exception e) {
log.warn("Dataframe decode error", e);
params = Collections.EMPTY_MAP;
}
}
if (log.isDebugEnabled()) {
log.debug("Dataframe: {} params: {}", onCueOrOnMeta, params.toString());
}
IoBuffer buf = IoBuffer.allocate(64);
buf.setAutoExpand(true);
Output out = new Output(buf);
out.writeString(onCueOrOnMeta);
out.writeMap(params);
buf.flip();
// instance a notify with action
ret = new Notify(buf, onCueOrOnMeta);
} else {
byte object = input.readDataType();
if (object == DataTypes.CORE_SWITCH) {
log.trace("Switching decoding to AMF3");
input = new org.red5.io.amf3.Input(in);
((org.red5.io.amf3.Input) input).enforceAMF3();
// re-read data type after switching decode
object = input.readDataType();
}
// onFI
// the onFI request contains 2 items relative to the publishing client application
// sd = system date (12-07-2011) st = system time (09:11:33.387)
log.info("Stream send: {}", action);
Map<Object, Object> params = Collections.EMPTY_MAP;
log.debug("Params type: {}", object);
if (object == DataTypes.CORE_MAP) {
params = (Map<Object, Object>) input.readMap();
if (log.isDebugEnabled()) {
log.debug("Map params: {}", params.toString());
}
} else if (object == DataTypes.CORE_ARRAY) {
params = (Map<Object, Object>) input.readArray(Object[].class);
if (log.isDebugEnabled()) {
log.debug("Array params: {}", params);
}
} else if (object == DataTypes.CORE_STRING) {
String str = input.readString();
if (log.isDebugEnabled()) {
log.debug("String params: {}", str);
}
params = new HashMap<>();
params.put("0", str);
} else if (object == DataTypes.CORE_OBJECT) {
params = (Map<Object, Object>) input.readObject();
if (log.isDebugEnabled()) {
log.debug("Object params: {}", params);
}
} else if (log.isDebugEnabled()) {
log.debug("Stream send did not provide a parameter map");
}
// need to debug this further
/*
* IoBuffer buf = IoBuffer.allocate(64); buf.setAutoExpand(true); Output out = null; if (encoding == Encoding.AMF3) { out = new org.red5.io.amf3.Output(buf); } else { out = new
* Output(buf); } out.writeString(action); out.writeMap(params); buf.flip(); // instance a notify with action ret = new Notify(buf, action);
*/
// go back to the beginning
in.reset();
// instance a notify with action
ret = new Notify(in.asReadOnlyBuffer(), action);
}
} else {
// go back to the beginning
in.reset();
// instance a notify
ret = new Notify(in.asReadOnlyBuffer());
}
return ret;
}
use of org.red5.io.amf3.Input in project red5-server-common by Red5.
the class RTMPProtocolDecoder method doDecodeSharedObject.
/**
* Perform the actual decoding of the shared object contents.
*
* @param so
* Shared object message
* @param in
* input buffer
* @param input
* Input object to be processed
*/
protected void doDecodeSharedObject(SharedObjectMessage so, IoBuffer in, Input input) {
// Parse request body
Input amf3Input = new org.red5.io.amf3.Input(in);
while (in.hasRemaining()) {
final ISharedObjectEvent.Type type = SharedObjectTypeMapping.toType(in.get());
if (type == null) {
in.skip(in.remaining());
return;
}
String key = null;
Object value = null;
final int length = in.getInt();
if (type == ISharedObjectEvent.Type.CLIENT_STATUS) {
// Status code
key = input.getString();
// Status level
value = input.getString();
} else if (type == ISharedObjectEvent.Type.CLIENT_UPDATE_DATA) {
key = null;
// Map containing new attribute values
final Map<String, Object> map = new HashMap<String, Object>();
final int start = in.position();
while (in.position() - start < length) {
String tmp = input.getString();
map.put(tmp, Deserializer.deserialize(input, Object.class));
}
value = map;
} else if (type != ISharedObjectEvent.Type.SERVER_SEND_MESSAGE && type != ISharedObjectEvent.Type.CLIENT_SEND_MESSAGE) {
if (length > 0) {
key = input.getString();
if (length > key.length() + 2) {
// determine if the object is encoded with amf3
byte objType = in.get();
in.position(in.position() - 1);
Input propertyInput;
if (objType == AMF.TYPE_AMF3_OBJECT && !(input instanceof org.red5.io.amf3.Input)) {
// The next parameter is encoded using AMF3
propertyInput = amf3Input;
} else {
// The next parameter is encoded using AMF0
propertyInput = input;
}
value = Deserializer.deserialize(propertyInput, Object.class);
}
}
} else {
final int start = in.position();
// the "send" event seems to encode the handler name as complete AMF string including the string type byte
key = Deserializer.deserialize(input, String.class);
// read parameters
final List<Object> list = new LinkedList<Object>();
while (in.position() - start < length) {
byte objType = in.get();
in.position(in.position() - 1);
// determine if the object is encoded with amf3
Input propertyInput;
if (objType == AMF.TYPE_AMF3_OBJECT && !(input instanceof org.red5.io.amf3.Input)) {
// The next parameter is encoded using AMF3
propertyInput = amf3Input;
} else {
// The next parameter is encoded using AMF0
propertyInput = input;
}
Object tmp = Deserializer.deserialize(propertyInput, Object.class);
list.add(tmp);
}
value = list;
}
so.addEvent(type, key, value);
}
}
use of org.red5.io.amf3.Input in project red5-server-common by Red5.
the class RTMPProtocolDecoder method decodeSharedObject.
/**
* {@inheritDoc}
*/
public ISharedObjectMessage decodeSharedObject(IoBuffer in) {
final Input input = new org.red5.io.amf.Input(in);
String name = input.getString();
// Read version of SO to modify
int version = in.getInt();
// Read persistence informations
boolean persistent = in.getInt() == 2;
// Skip unknown bytes
in.skip(4);
// create our shared object message
final SharedObjectMessage so = new SharedObjectMessage(null, name, version, persistent);
doDecodeSharedObject(so, in, input);
return so;
}
use of org.red5.io.amf3.Input in project red5-server-common by Red5.
the class RTMPProtocolDecoder method decodeMessage.
/**
* Decodes RTMP message event.
*
* @param conn
* RTMP connection
* @param header
* RTMP header
* @param in
* Input IoBuffer
* @return RTMP event
*/
public IRTMPEvent decodeMessage(RTMPConnection conn, Header header, IoBuffer in) {
IRTMPEvent message;
byte dataType = header.getDataType();
switch(dataType) {
case TYPE_AUDIO_DATA:
message = decodeAudioData(in);
message.setSourceType(Constants.SOURCE_TYPE_LIVE);
break;
case TYPE_VIDEO_DATA:
message = decodeVideoData(in);
message.setSourceType(Constants.SOURCE_TYPE_LIVE);
break;
case TYPE_AGGREGATE:
message = decodeAggregate(in);
break;
case // represents an SO in an AMF3 container
TYPE_FLEX_SHARED_OBJECT:
message = decodeFlexSharedObject(in);
break;
case TYPE_SHARED_OBJECT:
message = decodeSharedObject(in);
break;
case TYPE_FLEX_MESSAGE:
message = decodeFlexMessage(in);
break;
case TYPE_INVOKE:
message = decodeAction(conn.getEncoding(), in, header);
break;
case TYPE_FLEX_STREAM_SEND:
if (log.isTraceEnabled()) {
log.trace("Decoding flex stream send on stream id: {}", header.getStreamId());
}
// skip first byte
in.get();
// decode stream data; slice from the current position
message = decodeStreamData(in.slice());
break;
case TYPE_NOTIFY:
if (log.isTraceEnabled()) {
log.trace("Decoding notify on stream id: {}", header.getStreamId());
}
if (header.getStreamId().doubleValue() != 0.0d) {
message = decodeStreamData(in);
} else {
message = decodeAction(conn.getEncoding(), in, header);
}
break;
case TYPE_PING:
message = decodePing(in);
break;
case TYPE_BYTES_READ:
message = decodeBytesRead(in);
break;
case TYPE_CHUNK_SIZE:
message = decodeChunkSize(in);
break;
case TYPE_SERVER_BANDWIDTH:
message = decodeServerBW(in);
break;
case TYPE_CLIENT_BANDWIDTH:
message = decodeClientBW(in);
break;
case TYPE_ABORT:
message = decodeAbort(in);
break;
default:
log.warn("Unknown object type: {}", dataType);
message = decodeUnknown(dataType, in);
break;
}
// add the header to the message
message.setHeader(header);
return message;
}
Aggregations