Search in sources :

Example 1 with Input

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;
}
Also used : Input(org.red5.io.amf.Input) RandomAccessFile(java.io.RandomAccessFile) IOException(java.io.IOException) ByteBuffer(java.nio.ByteBuffer) IoBuffer(org.apache.mina.core.buffer.IoBuffer)

Example 2 with Input

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;
}
Also used : Notify(org.red5.server.net.rtmp.event.Notify) RTMPConnection(org.red5.server.net.rtmp.RTMPConnection) Encoding(org.red5.server.api.IConnection.Encoding) ProtocolException(org.red5.server.net.protocol.ProtocolException) IoBuffer(org.apache.mina.core.buffer.IoBuffer) Input(org.red5.io.object.Input) Output(org.red5.io.amf.Output) Map(java.util.Map) HashMap(java.util.HashMap)

Example 3 with Input

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);
    }
}
Also used : ISharedObjectEvent(org.red5.server.so.ISharedObjectEvent) LinkedList(java.util.LinkedList) Input(org.red5.io.object.Input) Map(java.util.Map) HashMap(java.util.HashMap)

Example 4 with Input

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;
}
Also used : Input(org.red5.io.object.Input) SharedObjectMessage(org.red5.server.so.SharedObjectMessage) ISharedObjectMessage(org.red5.server.so.ISharedObjectMessage) FlexSharedObjectMessage(org.red5.server.so.FlexSharedObjectMessage)

Example 5 with Input

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;
}
Also used : IRTMPEvent(org.red5.server.net.rtmp.event.IRTMPEvent)

Aggregations

Test (org.junit.Test)12 IoBuffer (org.apache.mina.core.buffer.IoBuffer)11 Input (org.red5.io.amf.Input)10 HashMap (java.util.HashMap)9 Input (org.red5.io.amf3.Input)9 Input (org.red5.io.object.Input)8 Map (java.util.Map)6 Input (com.google.cloud.video.livestream.v1.Input)5 LivestreamServiceClient (com.google.cloud.video.livestream.v1.LivestreamServiceClient)5 IOException (java.io.IOException)5 ObjectMap (org.red5.io.utils.ObjectMap)4 ProtocolException (org.red5.server.net.protocol.ProtocolException)4 OOBControlMessage (org.red5.server.messaging.OOBControlMessage)3 File (java.io.File)2 FileInputStream (java.io.FileInputStream)2 ByteBuffer (java.nio.ByteBuffer)2 ArrayList (java.util.ArrayList)2 LinkedList (java.util.LinkedList)2 Output (org.red5.io.amf.Output)2 Output (org.red5.io.amf3.Output)2