bool SDP::ParseSDP(SDP &sdp, string &raw) { //FINEST("%s", STR(raw)); //1. Reset sdp.Reset(); //2. Prepare the sections sdp[SDP_SESSION].IsArray(false); sdp[SDP_MEDIATRACKS].IsArray(true); //3. Split the raw content into lines replace(raw, "\r\n", "\n"); vector<string> lines; split(raw, "\n", lines); //4. Detect the media tracks indexes vector<uint32_t> trackIndexes; for (uint32_t i = 0; i < lines.size(); i++) { trim(lines[i]); if (lines[i].find("m=") == 0) { ADD_VECTOR_END(trackIndexes, i); } } if (trackIndexes.size() == 0) { FATAL("No tracks found"); return false; } //5. Parse the header if (!ParseSection(sdp[SDP_SESSION], lines, 0, trackIndexes[0])) { FATAL("Unable to parse header"); return false; } //6. Parse the media sections Variant media; for (uint32_t i = 0; i < trackIndexes.size() - 1; i++) { media.Reset(); media.IsArray(false); if (!ParseSection(media, lines, trackIndexes[i], trackIndexes[i + 1] - trackIndexes[i])) { FATAL("Unable to parse header"); return false; } sdp[SDP_MEDIATRACKS].PushToArray(media); } //7. Parse the last media section media.Reset(); media.IsArray(false); if (!ParseSection(media, lines, trackIndexes[(uint32_t) trackIndexes.size() - 1], (uint32_t) trackIndexes.size() - trackIndexes[(uint32_t) trackIndexes.size() - 1])) { FATAL("Unable to parse header"); return false; } sdp[SDP_MEDIATRACKS].PushToArray(media); return true; }
bool SDP::ParseSDPLineA(string &attributeName, Variant &value, string line) { string::size_type pos = line.find(':'); if ((pos == string::npos) || (pos == 0) || (pos == (line.size() - 1))) { attributeName = line; value = (bool)true; return true; } attributeName = line.substr(0, pos); string rawValue = line.substr(line.find(':') + 1); if (attributeName == "control") { value = rawValue; return true; } else if (attributeName == "maxprate") { value = (double) strtod(STR(rawValue), NULL); return true; } else if (attributeName.find("x-") == 0) { value = rawValue; return true; } else if (attributeName == "rtpmap") { //rtpmap:<payload type> <encoding name>/<clock rate>[/<encoding parameters>] vector<string> parts; split(rawValue, " ", parts); if (parts.size() != 2) return false; value["payloadType"] = (uint8_t) atoi(STR(parts[0])); split(parts[1], "/", parts); if ((parts.size() != 2) && (parts.size() != 3)) return false; value["encodingName"] = parts[0]; if (lowerCase((string) value["encodingName"]) == "h264") { value["encodingName"] = (uint64_t) CODEC_VIDEO_AVC; } else if ((string) lowerCase(value["encodingName"]) == "mpeg4-generic") { value["encodingName"] = (uint64_t) CODEC_AUDIO_AAC; } else { WARN("Invalid codec"); value.Reset(); return true; } value["clockRate"] = (uint32_t) atoi(STR(parts[1])); if (parts.size() == 3) { value["encodingParameters"] = parts[2]; } return true; } else if (attributeName == "fmtp") { replace(rawValue, "; ", ";"); vector<string> parts; split(rawValue, " ", parts); if (parts.size() != 2) return false; value["payloadType"] = (uint8_t) atoi(STR(parts[0])); map<string, string> temp = mapping(parts[1], ";", "=", false); FOR_MAP(temp, string, string, i) { value[MAP_KEY(i)] = MAP_VAL(i); }
void RTSPProtocol::GetStats(Variant &info, uint32_t namespaceId) { BaseProtocol::GetStats(info, namespaceId); info["streams"].IsArray(true); Variant si; if (GetApplication() != NULL) { StreamsManager *pStreamsManager = GetApplication()->GetStreamsManager(); map<uint32_t, BaseStream*> streams = pStreamsManager->FindByProtocolId(GetId()); FOR_MAP(streams, uint32_t, BaseStream *, i) { si.Reset(); MAP_VAL(i)->GetStats(si, namespaceId); info["streams"].PushToArray(si); }
bool PopStack(lua_State *pLuaState, Variant &variant) { variant.Reset(); variant.IsArray(true); while (lua_gettop(pLuaState) > 0) { Variant temp; if (!PopVariant(pLuaState, temp)) { FATAL("Unable to pop variant"); return false; } variant.PushToArray(temp); } return true; }
bool SDP::ParseTransportLine(string raw, Variant &result) { //raw = "MP2T/H2221/UDP;unicast;destination=192.168.1.150;client_port=11112,MP2T/H2221/UDP;multicast,RAW/RAW/UDP;unicast;destination=192.168.1.150;client_port=11112,RAW/RAW/UDP;multicast"; //raw = "MP2T/H2221/UDP;unicast;destination=192.168.1.150;client_port=11112," + raw; result.Reset(); result["original"] = raw; result["alternatives"].IsArray(true); vector<string> parts; split(raw, ",", parts); for (vector<string>::size_type i = 0; i < parts.size(); i++) { Variant temp; if (!ParseTransportLinePart(parts[i], temp)) { WARN("Invalid transport part: %s", STR(parts[i])); continue; } result["alternatives"].PushToArray(temp); } //FINEST("%s", STR(result.ToString())); return result["alternatives"].MapSize() != 0; }
bool RTMPProtocolSerializer::Deserialize(Header &header, IOBuffer &buffer, Variant &message) { message.Reset(); message[RM_HEADER] = header.GetVariant(); switch (H_MT(header)) { case RM_HEADER_MESSAGETYPE_NOTIFY: { return DeserializeNotify(buffer, message[RM_NOTIFY]); } case RM_HEADER_MESSAGETYPE_FLEXSTREAMSEND: { return DeserializeFlexStreamSend(buffer, message[RM_FLEXSTREAMSEND]); } case RM_HEADER_MESSAGETYPE_INVOKE: case RM_HEADER_MESSAGETYPE_FLEX: { message[RM_INVOKE][RM_INVOKE_IS_FLEX] = (bool)(H_MT(header) == RM_HEADER_MESSAGETYPE_FLEX); return DeserializeInvoke(buffer, message[RM_INVOKE]); } case RM_HEADER_MESSAGETYPE_FLEXSHAREDOBJECT: { //TODO: This is a hack. We store the data on RM_SHAREDOBJECT //instead of RM_FLEXSHAREDOBJECT //return DeserializeFlexSharedObject(buffer, message[RM_FLEXSHAREDOBJECT]); return DeserializeFlexSharedObject(buffer, message[RM_SHAREDOBJECT]); } case RM_HEADER_MESSAGETYPE_SHAREDOBJECT: { return DeserializeSharedObject(buffer, message[RM_SHAREDOBJECT]); } case RM_HEADER_MESSAGETYPE_USRCTRL: { return DeserializeUsrCtrl(buffer, message[RM_USRCTRL]); } case RM_HEADER_MESSAGETYPE_CHUNKSIZE: { return DeserializeChunkSize(buffer, message[RM_CHUNKSIZE]); } case RM_HEADER_MESSAGETYPE_ACK: { return DeserializeAck(buffer, message[RM_ACK]); } case RM_HEADER_MESSAGETYPE_WINACKSIZE: { return DeserializeWinAckSize(buffer, message[RM_WINACKSIZE]); } case RM_HEADER_MESSAGETYPE_PEERBW: { return DeserializePeerBW(buffer, message[RM_PEERBW]); } case RM_HEADER_MESSAGETYPE_ABORTMESSAGE: { return DeserializeAbortMessage(buffer, message[RM_ABORTMESSAGE]); } default: { FATAL("Invalid message type: %u %s", H_MT(header), STR(buffer)); return false; } } }
bool parseURI(string stringUri, URI &uri) { /* * schema://[username[:password]@]host[:port][/[path[?parameters]]] */ LOG_URI("------------------------"); LOG_URI("stringUri: `%s`", STR(stringUri)); string fullUri; string fullUriWithAuth = stringUri; string scheme; string authentication; string username; string password; string hostPort; string host; string portString; uint16_t port; bool portSpecified; string fullDocumentPathWithParameters; string fullDocumentPath; string fullParameters; string documentPath; string document; string documentWithFullParameters; Variant parameters; string::size_type cursor = 0; string::size_type pos = 0; //1. Reset uri.Reset(); //2. trim trim(stringUri); if (stringUri == "") { FATAL("Empty uri"); return false; } //2. Get the scheme and the default port pos = stringUri.find("://", cursor); if (pos == string::npos) { FATAL("Unable to determine scheme"); return false; } scheme = lowerCase(stringUri.substr(cursor, pos - cursor)); cursor = pos + 3; if (_schemeToPort.size() == 0) { _schemeToPort["http"] = 80; _schemeToPort["rtmpt"] = 80; _schemeToPort["rtmpte"] = 80; _schemeToPort["https"] = 443; _schemeToPort["rtmps"] = 443; _schemeToPort["rtsp"] = 554; _schemeToPort["rtmp"] = 1935; _schemeToPort["rtmpe"] = 1935; _schemeToPort["mms"] = 1755; } if (MAP_HAS1(_schemeToPort, scheme)) { port = _schemeToPort[scheme]; } // else { // FATAL("Scheme `%s` not supported", STR(scheme)); // return false; // } LOG_URI("scheme: %s; default port: %"PRIu16, STR(scheme), port); //3. get the authentication portion. the search starts from //where the scheme detection left and up to the first / character string::size_type limit = stringUri.find("/", cursor); bool hasAuthentication = false; pos = stringUri.find("@", cursor); if (pos != string::npos) { if (limit != string::npos) { hasAuthentication = pos<limit; } hasAuthentication = true; } if (hasAuthentication) { authentication = stringUri.substr(cursor, pos - cursor); fullUri = stringUri.substr(0, cursor); fullUri += stringUri.substr(pos + 1); cursor = pos + 1; } else { fullUri = fullUriWithAuth; } if (authentication != "") { pos = authentication.find(":"); if (pos != string::npos) { username = authentication.substr(0, pos); password = authentication.substr(pos + 1); } else { username = authentication; password = ""; } } LOG_URI("fullUri: `%s`; fullUriWithAuth: `%s`", STR(fullUri), STR(fullUriWithAuth)); LOG_URI("username: `%s`; password: `%s`", STR(username), STR(password)); //4. Get the host:port pos = stringUri.find("/", cursor); if (pos == string::npos) { hostPort = stringUri.substr(cursor); cursor = stringUri.size() - 1; fullDocumentPathWithParameters = "/"; } else { hostPort = stringUri.substr(cursor, pos - cursor); cursor = pos + 1; fullDocumentPathWithParameters = "/" + stringUri.substr(cursor); } trim(hostPort); if (hostPort == "") { FATAL("Invalid host:port specified"); return false; } pos = hostPort.find(":"); if (pos == string::npos) { host = hostPort; portSpecified = false; } else { host = hostPort.substr(0, pos); trim(host); portString = hostPort.substr(pos + 1); portSpecified = true; port = (uint16_t) atoi(STR(portString)); if (format("%"PRIu16, port) != portString) { FATAL("Invalid port number specified: `%s`", STR(portString)); return false; } } LOG_URI("host: %s; port: %"PRIu16"; portSpecified: %d", STR(host), port, portSpecified); //5. fullDocumentPathWithParameters fullDocumentPath = "/"; fullParameters = ""; documentPath = "/"; document = ""; documentWithFullParameters = ""; parameters.Reset(); parameters.IsArray(false); if (fullDocumentPathWithParameters != "/") { pos = fullDocumentPathWithParameters.find("?"); if (pos == string::npos) { fullDocumentPath = fullDocumentPathWithParameters; fullParameters = ""; } else { fullDocumentPath = fullDocumentPathWithParameters.substr(0, pos); fullParameters = fullDocumentPathWithParameters.substr(pos + 1); } trim(fullParameters); if (fullParameters != "") { vector<string> elements; split(fullParameters, "&", elements); for (uint32_t i = 0; i < elements.size(); i++) { string kvp = elements[i]; if (kvp == "") continue; string k = ""; string v = ""; pos = kvp.find("="); if (pos == string::npos) { k = kvp; v = ""; } else { k = kvp.substr(0, pos); v = kvp.substr(pos + 1); } if (k == "") continue; parameters[k] = v; } } for (string::size_type i = fullDocumentPath.size() - 1; i >= 0; i--) { if (fullDocumentPath[i] == '/') break; document = fullDocumentPath[i] + document; } documentPath = fullDocumentPath.substr(0, fullDocumentPath.size() - document.size()); documentWithFullParameters = document; if (fullParameters != "") documentWithFullParameters += "?" + fullParameters; } LOG_URI("fullDocumentPathWithParameters: `%s`", STR(fullDocumentPathWithParameters)); LOG_URI("fullDocumentPath: `%s`", STR(fullDocumentPath)); LOG_URI("fullParameters: `%s`", STR(fullParameters)); LOG_URI("documentPath: `%s`", STR(documentPath)); LOG_URI("document: `%s`", STR(document)); LOG_URI("documentWithFullParameters: `%s`", STR(documentWithFullParameters)); LOG_URI("parameters:"); #ifdef DEBUG_URI FOR_MAP(parameters, string, Variant, i) { LOG_URI("\t`%s`: `%s`", STR(MAP_KEY(i)), STR(MAP_VAL(i))); }
bool SDP::ParseTransportLinePart(string raw, Variant &result) { result.Reset(); result["original"] = raw; //1. split after ';' vector<string> parts; split(raw, ";", parts); //2. Construct the result for (uint32_t i = 0; i < parts.size(); i++) { string part = parts[i]; trim(part); if (part == "") continue; string::size_type pos = part.find('='); if (pos == string::npos) { result[lowerCase(part)] = (bool)true; continue; } result[lowerCase(part.substr(0, pos))] = part.substr(pos + 1); } vector<string> keys; ADD_VECTOR_END(keys, "client_port"); ADD_VECTOR_END(keys, "server_port"); ADD_VECTOR_END(keys, "interleaved"); for (uint32_t i = 0; i < keys.size(); i++) { string key = keys[i]; if (!result.HasKey(key)) continue; parts.clear(); raw = (string) result[key]; split(raw, "-", parts); if ((parts.size() != 2) && (parts.size() != 1)) { FATAL("Invalid transport line: %s", STR(raw)); return false; } string all = ""; uint16_t data = 0; uint16_t rtcp = 0; if (parts.size() == 2) { data = (uint16_t) atoi(STR(parts[0])); rtcp = (uint16_t) atoi(STR(parts[1])); if (((data % 2) != 0) || ((data + 1) != rtcp)) { FATAL("Invalid transport line: %s", STR(raw)); return false; } all = format("%"PRIu16"-%"PRIu16, data, rtcp); } else { data = (uint16_t) atoi(STR(parts[0])); all = format("%"PRIu16, data); rtcp = 0; } if (all != raw) { FATAL("Invalid transport line: %s", STR(raw)); return false; } result.RemoveKey(key); result[key]["data"] = (uint16_t) data; result[key]["rtcp"] = (uint16_t) rtcp; result[key]["all"] = all; } return true; }
bool PopVariant(lua_State *pLuaState, Variant &variant, int32_t idx, bool pop) { variant.Reset(); int32_t type = lua_type(pLuaState, idx); switch (type) { case LUA_TNIL: { variant.Reset(); if (pop) lua_remove(pLuaState, idx); break; } case LUA_TBOOLEAN: { variant = (bool)(lua_toboolean(pLuaState, idx) != 0); if (pop) lua_remove(pLuaState, idx); break; } case LUA_TNUMBER: { lua_Number luaNumber = lua_tonumber(pLuaState, idx); if (pop) lua_remove(pLuaState, idx); variant = (double) luaNumber; variant.Compact(); break; } case LUA_TSTRING: { string nullable = lua_tostring(pLuaState, idx); if (pop) lua_remove(pLuaState, idx); if (nullable == VAR_NULL_VALUE) variant = Variant(); else variant = nullable; break; } case LUA_TTABLE: { bool isArray = true; lua_pushnil(pLuaState); while (lua_next(pLuaState, idx) != 0) { Variant value; if (!PopVariant(pLuaState, value, lua_gettop(pLuaState))) return false; Variant key; if (!PopVariant(pLuaState, key, lua_gettop(pLuaState), false)) return false; variant[key] = value; isArray &= (key == _V_NUMERIC); } variant.IsArray(isArray); if (variant.HasKey(VAR_MAP_NAME)) { variant.SetTypeName(variant[VAR_MAP_NAME]); variant.RemoveKey(VAR_MAP_NAME); } else { variant.ConvertToTimestamp(); } if (pop) lua_remove(pLuaState, idx); break; } default: { WARN("Element type not supported: %d (0x%x)", type, type); return false; break; } } return true; }