/// <summary> /// Analyzes and responds if required to the given <see cref="RTMPPacket"/>. /// </summary> /// <param name="packet">The <see cref="RTMPPacket"/> to inspect amnd react to.</param> /// <returns>0 (false) for OK/Failed/error, 1 for 'Stop or Complete' (true)</returns> bool HandleInvoke(RTMPPacket packet) { bool ret = false; if (packet.m_body[0] != 0x02) // make sure it is a string method name we start with { Logger.Log("HandleInvoke: Sanity failed. no string method in invoke packet"); return false; } AMFObject obj = new AMFObject(); int nRes = obj.Decode(packet.m_body, 0, (int)packet.m_nBodySize, false); if (nRes < 0) { Logger.Log("HandleInvoke: error decoding invoke packet"); return false; } obj.Dump(); string method = obj.GetProperty(0).GetString(); double txn = obj.GetProperty(1).GetNumber(); Logger.Log(string.Format("server invoking <{0}>", method)); if (method == "_result") { string methodInvoked = m_methodCalls.Dequeue(); Logger.Log(string.Format("received result for method call <{0}>", methodInvoked)); if (methodInvoked == "connect") { if (!string.IsNullOrEmpty(Link.token)) { List<AMFObjectProperty> props = new List<AMFObjectProperty>(); obj.FindMatchingProperty("secureToken", props, int.MaxValue); if (props.Count > 0) { string decodedToken = Tea.Decrypt(props[0].GetString(), Link.token); SendSecureTokenResponse(decodedToken); } } SendServerBW(); if (!SkipCreateStream) { SendPing(3, 0, 300); SendCreateStream(); } if (!string.IsNullOrEmpty(Link.subscribepath)) SendFCSubscribe(Link.subscribepath); else if (Link.bLiveStream) SendFCSubscribe(Link.playpath); } else if (methodInvoked == "createStream") { m_stream_id = (int)obj.GetProperty(3).GetNumber(); SendPlay(); SendPing(3, (uint)m_stream_id, (uint)m_nBufferMS); } else if (methodInvoked == "play") { Playing = true; } } else if (method == "onBWDone") { if (m_nBWCheckCounter == 0) SendCheckBW(); } else if (method == "_onbwcheck") { SendCheckBWResult(txn); } else if (method == "_onbwdone") { if (m_methodCalls.Contains("_checkbw")) { string[] queue = m_methodCalls.ToArray(); m_methodCalls.Clear(); for (int i = 0; i < queue.Length; i++) if (queue[i] != "_checkbw") m_methodCalls.Enqueue(queue[i]); } } else if (method == "_error") { Logger.Log("rtmp server sent error"); } else if (method == "close") { Logger.Log("rtmp server requested close"); Close(); } else if (method == "onStatus") { string code = obj.GetProperty(3).GetObject().GetProperty("code").GetString(); string level = obj.GetProperty(3).GetObject().GetProperty("level").GetString(); Logger.Log(string.Format("onStatus: code :{0}, level: {1}", code, level)); if (code == "NetStream.Failed" || code == "NetStream.Play.Failed" || code == "NetStream.Play.StreamNotFound" || code == "NetConnection.Connect.InvalidApp") { Close(); } else if (code == "NetStream.Play.Start" || code == "NetStream.Publish.Start") { Playing = true; } else if (code == "NetStream.Play.Complete" || code == "NetStream.Play.Stop") { Close(); ret = true; } else if (code == "NetStream.Pause.Notify") { if (Pausing == 1 || Pausing == 2) { SendPause(false); Pausing = 3; } } } else if (MethodHookHandler != null) { ret = MethodHookHandler(method, obj, this); } else { } return ret; }
void HandleMetadata(byte[] buffer, int offset, int size) { AMFObject obj = new AMFObject(); int nRes = obj.Decode(buffer, offset, size, false); if (nRes < 0) { //Log(LOGERROR, "%s, error decoding meta data packet", __FUNCTION__); return; } if (!Playing) obj.Dump(); string metastring = obj.GetProperty(0).GetString(); if (metastring == "onMetaData") { if (Playing) obj.Dump(); // always dump metadata for further analyzing List<AMFObjectProperty> props = new List<AMFObjectProperty>(); obj.FindMatchingProperty("duration", props, 1); if (props.Count > 0) { Duration = props[0].GetNumber(); Logger.Log(string.Format("Set duration: {0}", Duration)); } props.Clear(); obj.FindMatchingProperty("audiodatarate", props, 1); if (props.Count > 0) { int audiodatarate = (int)props[0].GetNumber(); CombinedBitrates += audiodatarate; Logger.Log(string.Format("audiodatarate: {0}", audiodatarate)); } props.Clear(); obj.FindMatchingProperty("videodatarate", props, 1); if (props.Count > 0) { int videodatarate = (int)props[0].GetNumber(); CombinedBitrates += videodatarate; Logger.Log(string.Format("videodatarate: {0}", videodatarate)); } if (CombinedTracksLength == 0) { props.Clear(); obj.FindMatchingProperty("filesize", props, int.MaxValue); if (props.Count > 0) { CombinedTracksLength = (int)props[0].GetNumber(); Logger.Log(string.Format("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].GetNumber(); Logger.Log(string.Format("Set CombinedTracksLength from datasize: {0}", CombinedTracksLength)); } } } }
public bool GetExpectedPacket(string expectedMethod, out AMFObject obj) { RTMPPacket packet = null; obj = null; bool ready = false; while (!ready && IsConnected() && ReadPacket(out packet)) { if (!packet.IsReady()) continue; // keep reading until complete package has arrived if (packet.PacketType != PacketType.Invoke) Logger.Log(string.Format("Ignoring packet of type {0}", packet.PacketType)); else { if (packet.m_body[0] != 0x02) // make sure it is a string method name we start with { Logger.Log("GetExpectedPacket: Sanity failed. no string method in invoke packet"); return false; } obj = new AMFObject(); int nRes = obj.Decode(packet.m_body, 0, (int)packet.m_nBodySize, false); if (nRes < 0) { Logger.Log("GetExpectedPacket: error decoding invoke packet"); return false; } obj.Dump(); string method = obj.GetProperty(0).GetString(); double txn = obj.GetProperty(1).GetNumber(); Logger.Log(string.Format("server invoking <{0}>", method)); if (method == "_result" && m_methodCalls.Count > 0) { string methodInvoked = m_methodCalls.Dequeue(); Logger.Log(string.Format("received result for method call <{0}>", methodInvoked)); } ready = method == expectedMethod; } } return ready; }
public static AMFObject ParseAMF(string amfString) { int depth = 0; AMFObject obj = new AMFObject(); string[] args = amfString.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (string arg in args) { AMFObjectProperty prop = new AMFObjectProperty(); string p; if (arg[1] == ':') { p = arg.Substring(2); switch (arg[0]) { case 'B': prop.m_type = AMFDataType.AMF_BOOLEAN; prop.p_number = int.Parse(p); break; case 'S': prop.m_type = AMFDataType.AMF_STRING; prop.m_strVal = p; break; case 'N': prop.m_type = AMFDataType.AMF_NUMBER; prop.p_number = double.Parse(p); break; case 'Z': prop.m_type = AMFDataType.AMF_NULL; break; case 'O': int i = int.Parse(p); if (i > 0) { prop.m_type = AMFDataType.AMF_OBJECT; } else { depth--; return obj; } break; default: return null; } } else if (arg[2] == ':' && arg[0] == 'N') { int secondColonIndex = arg.IndexOf(':', 3); if (secondColonIndex < 0 || depth <= 0) return null; prop.m_strName = arg.Substring(3); p = arg.Substring(secondColonIndex + 1); switch (arg[1]) { case 'B': prop.m_type = AMFDataType.AMF_BOOLEAN; prop.p_number = int.Parse(p); break; case 'S': prop.m_type = AMFDataType.AMF_STRING; prop.m_strVal = p; break; case 'N': prop.m_type = AMFDataType.AMF_NUMBER; prop.p_number = double.Parse(p); break; case 'O': prop.m_type = AMFDataType.AMF_OBJECT; break; default: return null; } } else return null; if (depth > 0) { AMFObject o2; for (int i = 0; i < depth; i++) { o2 = obj.GetProperty(obj.GetPropertyCount() - 1).GetObject(); obj = o2; } } obj.AddProperty(prop); if (prop.m_type == AMFDataType.AMF_OBJECT) depth++; } return obj; }