public void Clear() { Event = string.Empty; Code = string.Empty; Level = string.Empty; EventInfo = null; }
public void Reset() { numVal = 0.0; stringVal = ""; objVal = null; type = AMFDataType.AMF_INVALID; }
public object Clone() { AMFObject clone = new AMFObject(); foreach (AMFObjectProperty prop in properties) { clone.properties.Add((AMFObjectProperty)prop.Clone()); } //foreach return clone; }
public object Clone() { AMFObject clone = new AMFObject(); foreach (AMFObjectProperty prop in properties) { clone.properties.Add((AMFObjectProperty)prop.Clone()); } //foreach return(clone); }
/// <summary> /// Decode the amfobject when NetConnection has a successfull connnection. /// Don't know if it's of any use and if the same info is available for /// different RTMP servers. (Only tested with wowza 3.5.2) /// </summary> /// <param name="obj"></param> private void DecodeNetConnectionInfo_Connect_Result(AMFObject obj) { netConnectionConnectInfo.Clear(); // WOWZA: In position 2 and 3 is the info we want as // Red5: has all it's info in [3] it seems if (obj.Count < 3) { return; } List<AMFObjectProperty> props = new List<AMFObjectProperty>(); props.Clear(); obj.FindMatchingProperty("fmsVer", props, 1); if (props.Count > 0) { netConnectionConnectInfo.FMSVer = props[0].StringValue; } props.Clear(); obj.FindMatchingProperty("capabilities", props, 1); if (props.Count > 0) { try { netConnectionConnectInfo.Capabilities = Convert.ToInt32(props[0].NumberValue); } catch { } } props.Clear(); obj.FindMatchingProperty("mode", props, 1); if (props.Count > 0) { try { netConnectionConnectInfo.Mode = Convert.ToInt32(props[0].NumberValue); } catch { } } props.Clear(); obj.FindMatchingProperty("code", props, 1); if (props.Count > 0) { netConnectionConnectInfo.Code = props[0].StringValue; } props.Clear(); obj.FindMatchingProperty("level", props, 1); if (props.Count > 0) { netConnectionConnectInfo.Level = props[0].StringValue; } props.Clear(); obj.FindMatchingProperty("description", props, 1); if (props.Count > 0) { netConnectionConnectInfo.Description = props[0].StringValue; } props.Clear(); obj.FindMatchingProperty("data", props, 1); if (props.Count > 0) { AMFObject obj2 = props[0].ObjectValue; props.Clear(); obj2.FindMatchingProperty("version", props, 1); if (props.Count > 0) { try { netConnectionConnectInfo.Version = new Version(props[0].StringValue.Replace(',', '.')); realRTMPServerVersion = netConnectionConnectInfo.Version; } catch { } } } // Red5 doesn't seem to have this property props.Clear(); obj.FindMatchingProperty("clientid", props, 1); if (props.Count > 0) { try { netConnectionConnectInfo.ClientID = Convert.ToInt64(props[0].NumberValue); } catch { } } // Red5 doesn't seem to have this property props.Clear(); obj.FindMatchingProperty("objectEncoding", props, 1); if (props.Count > 0) { try { netConnectionConnectInfo.ObjectEncoding = Convert.ToInt32(props[0].NumberValue); } catch { } } }
/// <summary> /// Analyzes and responds if required to the given <see cref="RTMPPacket"/>. /// </summary> /// <param name="packet">The packet to inspect and react to.</param> /// <returns>0 (false) for OK/Failed/error, 1 for 'Stop or Complete' (true)</returns> private bool HandleInvoke(RTMPPacket packet) { bool ret = false; if (packet.Body[0] != 0x02) // make sure it is a string method name we start with { LibRTMPLogger.Log(LibRTMPLogLevel.Warning, "[CDR.LibRTMP.NetConnection.HandleInvoke] Sanity failed. no string method in invoke packet"); return false; } AMFObject obj = new AMFObject(); int nRes = obj.Decode(packet.Body, 0, (int)packet.BodySize, false); if (nRes < 0) { LibRTMPLogger.Log(LibRTMPLogLevel.Warning, "[CDR.LibRTMP.NetConnection.HandleInvoke] Error decoding invoke packet"); return false; } obj.Dump(); string method = obj.GetProperty(0).StringValue; double txn = obj.GetProperty(1).NumberValue; LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandleInvoke] Server invoking <{0}>", method)); if (method == "_result") { int transactionResultNum = (int)obj.GetProperty(1).NumberValue; string methodInvoked = ""; if (methodCallDictionary.ContainsKey(transactionResultNum)) { methodInvoked = methodCallDictionary[transactionResultNum]; methodCallDictionary.Remove(transactionResultNum); } LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandleInvoke] received result for method call <{0}>", methodInvoked)); if (methodInvoked == "connect") { // Get some info out of the result connection DecodeNetConnectionInfo_Connect_Result(obj); // Is SecureToken activate (when using wowza server) string tmpSecureTokenPassword = string.Empty; lock (lockVAR) { tmpSecureTokenPassword = secureTokenPassword; } if (!string.IsNullOrEmpty(tmpSecureTokenPassword)) { List<AMFObjectProperty> props = new List<AMFObjectProperty>(); obj.FindMatchingProperty("secureToken", props, int.MaxValue); if (props.Count > 0) { #if INCLUDE_TMPE #endif } } MQInternal_SendServerBW(); // Send OnConnect event } else if (methodInvoked == "createStream") { int transactionNum = (int)obj.GetProperty(1).NumberValue; int stream_id = (int)obj.GetProperty(3).NumberValue; if (transactionIDReferenceTable.ContainsKey(transactionNum) && transactionIDReferenceTable[transactionNum] is NetStream) { NetStream netStream = (NetStream)transactionIDReferenceTable[transactionNum]; transactionIDReferenceTable.Remove(transactionNum); LibRTMPLogger.Log(LibRTMPLogLevel.Info, string.Format("[CDR.LibRTMP.NetConnection.HandleInvoke] Received createStream(stream_id={0})", stream_id)); netStream.Stream_ID = stream_id; // make sure we know which NetStreams use this NetCOnnection RegisterNetStream(netStream); int contentBufferTime = NetConnection.DefaultContentBufferTime; netStream.HandleOnAssignStream_ID(packet, stream_id, out contentBufferTime); if (contentBufferTime <= 0) { contentBufferTime = NetConnection.DefaultContentBufferTime; } // Tell buffer time we want to use for this channel MQ_SendPing(3, (uint)netStream.Stream_ID, (uint)contentBufferTime); } else { // We haven't found a NetStream for which this is intended, so delete it again MQ_SendDeleteStream(stream_id); } } else if (methodInvoked == "play") { // Server send the play command? } } else if (method == "onBWDone") { if (bwCheckCounter == 0) { MQInternal_SendCheckBW(); } } else if (method == "_onbwcheck") { MQInternal_SendCheckBWResult(txn); } else if (method == "_onbwdone") { int transactionResultNum = (int)obj.GetProperty(1).NumberValue; if (methodCallDictionary.ContainsValue("_checkbw")) { var item = methodCallDictionary.First(x => x.Value == "_checkbw"); methodCallDictionary.Remove(item.Key); } } else if (method == "_error") { LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "[CDR.LibRTMP.NetConnection.HandleInvoke] rtmp server sent error"); } else if (method == "close") { LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "[CDR.LibRTMP.NetConnection.HandleInvoke] rtmp server requested close"); CloseConnection(); } else if (method == "onStatus") { int stream_id = packet.InfoField2; string code = obj[3].ObjectValue.GetProperty("code").StringValue; string level = obj[3].ObjectValue.GetProperty("level").StringValue; LibRTMPLogger.Log(LibRTMPLogLevel.Info, string.Format("[CDR.LibRTMP.NetConnection.HandleInvoke] stream_id={0}, method={1}, code={2}, level={3}", stream_id, method, code, level)); // Zoek NetStream op en geef OnStatus door if (netStreams[stream_id] != null) { if (code == "NetStream.Pause.Notify") { // fix to help netstream packet.TimeStamp = channelTimestamp[netStreams[stream_id].MediaChannel]; } netStreams[stream_id].HandleOnStatus(packet); } else { LibRTMPLogger.Log(LibRTMPLogLevel.Info, string.Format("[CDR.LibRTMP.NetConnection.HandleInvoke] UNHANDLED | stream_id={0}, method={1}, code={2}, level={3}", stream_id, method, code, level)); } } else if (dMethodLookup.ContainsKey(method)) { ret = true; SynchronizationContext sc; State_NC_MethodCall stateMethodCall = new State_NC_MethodCall(); lock (lockVAR) { sc = synchronizationContext; stateMethodCall.Call = dMethodLookup[method]; } //lock stateMethodCall.thisObject = this; stateMethodCall.MethodParam = obj; if (sc != null) { switch (synchronizationContextMethod) { case SynchronizationContextMethod.Post: sc.Post(HandleOnMethodCall, stateMethodCall); break; case SynchronizationContextMethod.Send: sc.Send(HandleOnMethodCall, stateMethodCall); break; } //switch } else { HandleOnMethodCall(stateMethodCall); } } else { LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.HandleInvoke] [EVENT]={0}", method)); } return ret; }
private void HandleMetadata(RTMPPacket packet) { int stream_id = packet.InfoField2; // Find matching NetStream and forward this packet. if (netStreams[stream_id] == null) { LibRTMPLogger.Log(LibRTMPLogLevel.Warning, string.Format("[CDR.LibRTMP.NetConnection.HandleMetadata] No matching NetStream found for stream_id {0}", stream_id)); return; } AMFObject obj = new AMFObject(); int nRes = obj.Decode(packet.Body, 0, (int)packet.BodySize, false); if (nRes < 0) { LibRTMPLogger.Log(LibRTMPLogLevel.Warning, "[CDR.LibRTMP.NetConnection.HandleMetadata] Error decoding meta data packet"); return; } string metastring = obj.GetProperty(0).StringValue; switch (metastring) { case "|RtmpSampleAccess": // Ignore this, unknown return; // Looks more as an invoke to me Let NetStream.OnStatus handle it case "onStatus": // -=> "NetStream.Data.Start" case "onPlayStatus": // -=> "NetStream.Play.Switch" case "onMetaData": // This seams to me te real metadata case "onID3": // Let NetStream Handle it netStreams[stream_id].HandleOnMetaData(packet); break; default: LibRTMPLogger.Log(LibRTMPLogLevel.Warning, string.Format("[CDR.LibRTMP.NetConnection.HandleMetadata] Unknown metadata {0}", metastring)); break; } //switch return; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] // hide it for code completion virtual internal bool HandleOnMetaData(RTMPPacket packet) { AMFObject obj = new AMFObject(); int nRes = obj.Decode(packet.Body, 0, (int)packet.BodySize, false); if (nRes < 0) { LibRTMPLogger.Log(LibRTMPLogLevel.Warning, "[CDR.LibRTMP.NetStream.HandleOnMetaData] Error decoding meta data packet"); return false; } /* For video: * canSeekToEnd = true * videocodecid = 4 * framerate = 15 * videodatarate = 400 * height = 215 * width = 320 * duration = 7.347 * * For Audio (MP3): metastring =="onID3" * */ string metastring = obj.GetProperty(0).StringValue; switch (metastring) { case "onMetaData": List<AMFObjectProperty> props = new List<AMFObjectProperty>(); props.Clear(); obj.FindMatchingProperty("audiodatarate", props, 1); if (props.Count > 0) { int rate = (int)props[0].NumberValue; audioDatarate += rate; LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetStream.HandleOnMetaData] audiodatarate: {0}", audioDatarate)); } props.Clear(); obj.FindMatchingProperty("videodatarate", props, 1); if (props.Count > 0) { int rate = (int)props[0].NumberValue; videoDatarate += rate; LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetStream.HandleOnMetaData] videodatarate: {0}", videoDatarate)); } if (audioDatarate == 0 && videoDatarate == 0) { props.Clear(); obj.FindMatchingProperty("filesize", props, int.MaxValue); if (props.Count > 0) { combinedTracksLength = (int)props[0].NumberValue; LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetStream.HandleOnMetaData] Set CombinedTracksLength from filesize: {0}", combinedTracksLength)); } } if (combinedTracksLength == 0) { props.Clear(); obj.FindMatchingProperty("datasize", props, int.MaxValue); if (props.Count > 0) { combinedTracksLength = (int)props[0].NumberValue; LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetStream.HandleOnMetaData] Set CombinedTracksLength from datasize: {0}", combinedTracksLength)); } } props.Clear(); obj.FindMatchingProperty("duration", props, 1); if (props.Count > 0) { double duration = props[0].NumberValue; LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetStream.HandleOnMetaData] Set duration: {0}", duration)); lock (lockVAR) { metaDataDurationInMS = Convert.ToInt64(duration * 1000); } // Looks the same as the "onPlayStatus" (See NetConnection.HandleMetadata (we route it there through HandleOnStatusDecoded // doing it here also) Internal_HandleOnStatusDecoded(packet, "onStatus", "NetStream.Play.OnMetaData", "onPlayStatus", obj); } break; // Looks more as an invoke to me Let NetStream.OnStatus handle it case "onStatus": // -=> "NetStream.Data.Start" Internal_HandleOnStatusDecoded(packet, "onStatus", obj.GetProperty(1).ObjectValue.GetProperty("code").StringValue, "", obj); break; case "onPlayStatus": // "code" = "NetStream.Play.Switch" // "level"= "status" ,made it "onPlayStatus" // Has also "duration" and "bytes" as additional metadata (looks like a normal onMetaData to me with less options as the normal) Internal_HandleOnStatusDecoded(packet, "onStatus", obj.GetProperty(1).ObjectValue.GetProperty("code").StringValue, "onPlayStatus", obj); break; case "onID3": return HandleOnID3(obj); default: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetConnection.NetStream.HandleOnMetaData] metastring= {0}", metastring)); break; } //switch return true; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] // hide it for code completion virtual protected bool Internal_HandleOnStatusDecoded(RTMPPacket packet, string eventStr, string codeStr, string levelStr, AMFObject obj) { //LibRTMPLogger.Log(LibRTMPLogLevel.Error, string.Format("[CDR.LibRTMP.NetStream.Internal_HandleOnStatusDecoded] event={0} code={1} level={2} Timestamp={3}", eventStr, codeStr, levelStr, packet.TimeStamp)); //Console.WriteLine(string.Format("event={0} code={1} level={2} Timestamp={3} seekIsActive={4}", eventStr, codeStr, levelStr, packet.TimeStamp, seekIsActive.ToString())); if (OnStatus != null) { NetStreamStatusEvent netStreamStatusEvent = new NetStreamStatusEvent(); netStreamStatusEvent.Clear(); netStreamStatusEvent.Event = eventStr; netStreamStatusEvent.Code = codeStr; netStreamStatusEvent.Level = levelStr; netStreamStatusEvent.EventInfo = (AMFObject)obj.Clone(); // for thread safety MQ_RTMPMessage message = new MQ_RTMPMessage(); message.MethodCall = MethodCall.OnEventCallUserCode; message.Params = new object[] { OnStatus, this, netStreamStatusEvent }; netConnection.PostOnEventUserCallCodeMessage(message); } // Make sure we point to the right record which is buffered! if (codeStr == "NetStream.Play.Switch") { // end of stream (adjust duration to correct for inaccuracy!) lock (lockVAR) { seekIsActive = false; } //lock } // Now do our thing switch (codeStr) { // We need to flush the existing buffers and wait until // new data arrives case "NetStream.Seek.Notify": lock (lockVAR) { seekIsActive = true; // is probably already set blockMediaPackets++; deltaTimeStampInMS = packet.TimeStamp; } Internal_OnSeekNotify(packet.TimeStamp); break; case "NetStream.Play.Reset": // send when playlist starts at the beginning lock (lockVAR) { atBeginOfAudio = true; mediaBytesReceived = 0; deltaTimeStampInMS = 0; } //lock break; case "NetStream.Play.Switch": // Tell we have to stop playing (and drain the buffers!) if (mediaBytesReceived > 0) // only when we are streaming already { blockMediaPackets++; } break; case "NetStream.Data.Start": mediaBytesReceived = 0; break; // stream begins to play (that is data is send) // deblock if needed case "NetStream.Play.Start": lock (lockVAR) { if (blockMediaPackets > 0) { blockMediaPackets--; } seekIsActive = false; // is probably already set } //lock break; case "NetStream.Play.Stop": ReplaySavedPackets(); // needed in case packets where saved (we're at the end so a byte sync will not occure anymore) lock (lockVAR) { atBeginOfAudio = true; } break; // Pause logic case "NetStream.Pause.Notify": ReplaySavedPackets(); // needed in case packets where saved (we're at the end so a byte sync will not occure anymore) lock (lockVAR) { if (mediaBytesReceived > 0) // we're buffering { pauseIsActive = true; Internal_OnPauseStream(true); if (OnPauseStream != null) { MQ_RTMPMessage message = new MQ_RTMPMessage(); message.MethodCall = MethodCall.OnEventCallUserCode; message.Params = new object[] { OnPauseStream, this, true }; netConnection.PostOnEventUserCallCodeMessage(message); } } } //lock break; case "NetStream.Unpause.Notify": lock (lockVAR) { syncAfterPauseNeeded = true; pauseIsActive = false; Internal_OnPauseStream(false); if (OnPauseStream != null) { MQ_RTMPMessage message = new MQ_RTMPMessage(); message.MethodCall = MethodCall.OnEventCallUserCode; message.Params = new object[] { OnPauseStream, this, false }; netConnection.PostOnEventUserCallCodeMessage(message); } } //lock break; case "NetStream.Play.Failed": case "NetStream.Play.StreamNotFound": case "NetStream.Failed": break; case "NetStream.Play.Complete": // all data is send lock (lockVAR) { seekIsActive = false; // safety } break; } //switch // Code can be eg: // "NetStream.Failed" // "NetStream.Play.Failed" // "NetStream.Play.StreamNotFound" // "NetConnection.Connect.InvalidApp" // "NetStream.Play.Start" // "NetStream.Publish.Start" // "NetStream.Play.Complete" //audio // "NetStream.Play.Stop" // audio // "NetStream.Pause.Notify" // "NetStream.Seek.Notify" return true; }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] // hide it for code completion internal bool HandleOnStatus(RTMPPacket packet) { AMFObject obj = new AMFObject(); int nRes = obj.Decode(packet.Body, 0, (int)packet.BodySize, false); if (nRes < 0) { return false; } // string method = obj.GetProperty(0).GetString(); // always onStatus int stream_id = packet.InfoField2; string code = obj.GetProperty(3).ObjectValue.GetProperty("code").StringValue; string level = obj.GetProperty(3).ObjectValue.GetProperty("level").StringValue; LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("[CDR.LibRTMP.NetStream.HandleOnStatus] code :{0}, level: {1}", code, level)); return Internal_HandleOnStatusDecoded(packet, "onStatus", code, level, obj); }
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] // hide it for code completion virtual internal bool HandleOnID3(AMFObject obj) { LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "[CDR.LibRTMP.NetConnection.NetStream.HandleOnID3]"); obj.Dump(); audioMetaData.Clear(); audioMetaData.Valid = true; List<AMFObjectProperty> props = new List<AMFObjectProperty>(); props.Clear(); obj.FindMatchingProperty("v1SongTitle", props, 1); if (props.Count > 0) { audioMetaData.SongTitle = props[0].StringValue; } props.Clear(); obj.FindMatchingProperty("v1LeadArtist", props, 1); if (props.Count > 0) { audioMetaData.LeadArtist = props[0].StringValue; } props.Clear(); obj.FindMatchingProperty("v1AlbumTitle", props, 1); if (props.Count > 0) { audioMetaData.AlbumTitle = props[0].StringValue; } props.Clear(); obj.FindMatchingProperty("v1YearReleased", props, 1); if (props.Count > 0) { audioMetaData.YearReleased = props[0].StringValue; } props.Clear(); obj.FindMatchingProperty("v1SongComment", props, 1); if (props.Count > 0) { audioMetaData.SongComment = props[0].StringValue; } props.Clear(); obj.FindMatchingProperty("v1SongGenre", props, 1); if (props.Count > 0) { audioMetaData.SongGenre = props[0].StringValue; } props.Clear(); obj.FindMatchingProperty("v1TrackNumberOnAlbum", props, 1); if (props.Count > 0) { audioMetaData.TrackNumberOnAlbum = props[0].StringValue; } // handle event if (OnID3 != null) { MQ_RTMPMessage message = new MQ_RTMPMessage(); message.MethodCall = MethodCall.OnEventCallUserCode; message.Params = new object[] { OnID3, this, (AudioMetaData)audioMetaData.Clone(), obj }; netConnection.PostOnEventUserCallCodeMessage(message); } return true; }
public int Decode(byte[] buffer, int bufferOffset, int size, bool bDecodeName) { int originalSize = size; if (size == 0 || buffer == null) { return(-1); } if (buffer[bufferOffset] == 0x05) { type = AMFDataType.AMF_NULL; return(1); } if (bDecodeName && size < 4) // at least name (length + at least 1 byte) and 1 byte of data { return(-1); } if (bDecodeName) { ushort nNameSize = RTMPHelper.ReadInt16(buffer, bufferOffset); if (nNameSize > size - (short)sizeof(short)) { return(-1); } stringName = RTMPHelper.ReadString(buffer, bufferOffset); size -= sizeof(short) + stringName.Length; bufferOffset += sizeof(short) + stringName.Length; } if (size == 0) { return(-1); } size--; int stringSize = 0; int result = 0; switch (buffer[bufferOffset]) { case (byte)AMFDataType.AMF_NUMBER: if (size < (int)sizeof(double)) { return(-1); } numVal = RTMPHelper.ReadNumber(buffer, bufferOffset + 1); size -= sizeof(double); type = AMFDataType.AMF_NUMBER; break; case (byte)AMFDataType.AMF_BOOLEAN: if (size < 1) { return(-1); } numVal = Convert.ToDouble(RTMPHelper.ReadBool(buffer, bufferOffset + 1)); size--; type = AMFDataType.AMF_BOOLEAN; break; case (byte)AMFDataType.AMF_STRING: stringSize = RTMPHelper.ReadInt16(buffer, bufferOffset + 1); if (size < stringSize + (int)sizeof(short)) { return(-1); } stringVal = RTMPHelper.ReadString(buffer, bufferOffset + 1); size -= sizeof(short) + stringSize; type = AMFDataType.AMF_STRING; break; case (byte)AMFDataType.AMF_OBJECT: objVal = new AMFObject(); result = objVal.Decode(buffer, bufferOffset + 1, size, true); if (result == -1) { return(-1); } size -= result; type = AMFDataType.AMF_OBJECT; break; case (byte)AMFDataType.AMF_MOVIECLIP: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "AMF_MOVIECLIP reserved!"); return(-1); case (byte)AMFDataType.AMF_NULL: case (byte)AMFDataType.AMF_UNDEFINED: case (byte)AMFDataType.AMF_UNSUPPORTED: type = AMFDataType.AMF_NULL; break; case (byte)AMFDataType.AMF_REFERENCE: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "AMF_REFERENCE not supported!"); return(-1); case (byte)AMFDataType.AMF_ECMA_ARRAY: size -= 4; // next comes the rest, mixed array has a final 0x000009 mark and names, so its an object objVal = new AMFObject(); result = objVal.Decode(buffer, bufferOffset + 5, size, true); if (result == -1) { return(-1); } size -= result; type = AMFDataType.AMF_OBJECT; break; case (byte)AMFDataType.AMF_OBJECT_END: return(-1); case (byte)AMFDataType.AMF_STRICT_ARRAY: int nArrayLen = RTMPHelper.ReadInt32(buffer, bufferOffset + 1); size -= 4; objVal = new AMFObject(); result = objVal.DecodeArray(buffer, bufferOffset + 5, size, nArrayLen, false); if (result == -1) { return(-1); } size -= result; type = AMFDataType.AMF_OBJECT; break; case (byte)AMFDataType.AMF_DATE: if (size < 10) { return(-1); } date = RTMPHelper.ReadNumber(buffer, bufferOffset + 1); dateUTCOffset = RTMPHelper.ReadInt16(buffer, bufferOffset + 9); size -= 10; break; case (byte)AMFDataType.AMF_LONG_STRING: stringSize = RTMPHelper.ReadInt32(buffer, bufferOffset + 1); if (size < stringSize + 4) { return(-1); } stringVal = RTMPHelper.ReadLongString(buffer, bufferOffset + 1); size -= (4 + stringSize); type = AMFDataType.AMF_STRING; break; case (byte)AMFDataType.AMF_RECORDSET: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "AMFObjectProperty.Decode AMF_RECORDSET reserved!"); return(-1); case (byte)AMFDataType.AMF_XML_DOC: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "AMFObjectProperty.Decode AMF_XML_DOC not supported!"); return(-1); case (byte)AMFDataType.AMF_TYPED_OBJECT: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "AMFObjectProperty.Decode AMF_TYPED_OBJECT not supported!"); return(-1); default: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("AMFObjectProperty.Decode Unknown datatype {0}", buffer[bufferOffset])); return(-1); } //switch return(originalSize - size); }
public int Decode(byte[] buffer, int bufferOffset, int size, bool bDecodeName) { int originalSize = size; if (size == 0 || buffer == null) { return -1; } if (buffer[bufferOffset] == 0x05) { type = AMFDataType.AMF_NULL; return 1; } if (bDecodeName && size < 4) // at least name (length + at least 1 byte) and 1 byte of data { return -1; } if (bDecodeName) { ushort nNameSize = RTMPHelper.ReadInt16(buffer, bufferOffset); if (nNameSize > size - (short)sizeof(short)) { return -1; } stringName = RTMPHelper.ReadString(buffer, bufferOffset); size -= sizeof(short) + stringName.Length; bufferOffset += sizeof(short) + stringName.Length; } if (size == 0) { return -1; } size--; int stringSize = 0; int result = 0; switch (buffer[bufferOffset]) { case (byte)AMFDataType.AMF_NUMBER: if (size < (int)sizeof(double)) { return -1; } numVal = RTMPHelper.ReadNumber(buffer, bufferOffset + 1); size -= sizeof(double); type = AMFDataType.AMF_NUMBER; break; case (byte)AMFDataType.AMF_BOOLEAN: if (size < 1) { return -1; } numVal = Convert.ToDouble(RTMPHelper.ReadBool(buffer, bufferOffset + 1)); size--; type = AMFDataType.AMF_BOOLEAN; break; case (byte)AMFDataType.AMF_STRING: stringSize = RTMPHelper.ReadInt16(buffer, bufferOffset + 1); if (size < stringSize + (int)sizeof(short)) { return -1; } stringVal = RTMPHelper.ReadString(buffer, bufferOffset + 1); size -= sizeof(short) + stringSize; type = AMFDataType.AMF_STRING; break; case (byte)AMFDataType.AMF_OBJECT: objVal = new AMFObject(); result = objVal.Decode(buffer, bufferOffset + 1, size, true); if (result == -1) { return -1; } size -= result; type = AMFDataType.AMF_OBJECT; break; case (byte)AMFDataType.AMF_MOVIECLIP: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "AMF_MOVIECLIP reserved!"); return -1; case (byte)AMFDataType.AMF_NULL: case (byte)AMFDataType.AMF_UNDEFINED: case (byte)AMFDataType.AMF_UNSUPPORTED: type = AMFDataType.AMF_NULL; break; case (byte)AMFDataType.AMF_REFERENCE: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "AMF_REFERENCE not supported!"); return -1; case (byte)AMFDataType.AMF_ECMA_ARRAY: size -= 4; // next comes the rest, mixed array has a final 0x000009 mark and names, so its an object objVal = new AMFObject(); result = objVal.Decode(buffer, bufferOffset + 5, size, true); if (result == -1) { return -1; } size -= result; type = AMFDataType.AMF_OBJECT; break; case (byte)AMFDataType.AMF_OBJECT_END: return -1; case (byte)AMFDataType.AMF_STRICT_ARRAY: int nArrayLen = RTMPHelper.ReadInt32(buffer, bufferOffset + 1); size -= 4; objVal = new AMFObject(); result = objVal.DecodeArray(buffer, bufferOffset + 5, size, nArrayLen, false); if (result == -1) { return -1; } size -= result; type = AMFDataType.AMF_OBJECT; break; case (byte)AMFDataType.AMF_DATE: if (size < 10) { return -1; } date = RTMPHelper.ReadNumber(buffer, bufferOffset + 1); dateUTCOffset = RTMPHelper.ReadInt16(buffer, bufferOffset + 9); size -= 10; break; case (byte)AMFDataType.AMF_LONG_STRING: stringSize = RTMPHelper.ReadInt32(buffer, bufferOffset + 1); if (size < stringSize + 4) { return -1; } stringVal = RTMPHelper.ReadLongString(buffer, bufferOffset + 1); size -= (4 + stringSize); type = AMFDataType.AMF_STRING; break; case (byte)AMFDataType.AMF_RECORDSET: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "AMFObjectProperty.Decode AMF_RECORDSET reserved!"); return -1; case (byte)AMFDataType.AMF_XML_DOC: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "AMFObjectProperty.Decode AMF_XML_DOC not supported!"); return -1; case (byte)AMFDataType.AMF_TYPED_OBJECT: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, "AMFObjectProperty.Decode AMF_TYPED_OBJECT not supported!"); return -1; default: LibRTMPLogger.Log(LibRTMPLogLevel.Trace, string.Format("AMFObjectProperty.Decode Unknown datatype {0}", buffer[bufferOffset])); return -1; } //switch return originalSize - size; }