package org.kafecho.learning.parser import scala.util.parsing.combinator.RegexParsers import java.net.URI sealed trait SdpValue sealed trait Username case class UserLogin(login: String) extends Username case object UserIdsNotSupported extends Username sealed trait Media case object Audio extends Media case object Video extends Media case object Text extends Media case object Application extends Media sealed trait Attribute case class BinaryAttribute(value: String) extends Attribute case class KeyValueAttribute(key: String, value: String) extends Attribute case class ProtocolVersion(version: Long) extends SdpValue case class Origin(username: Username, sessionId: Long, sessionVersion: Long, netType: String, addrType: String, unicastAddress: Option[String]) extends SdpValue case class SessionName(name: String) extends SdpValue case class Information(information: String) extends SdpValue case class Protocol(protocol: String) case class MediaField(media: Media, port: Long, protocol: Protocol, payloadType: Long) /** Prototype parser for the Session Description Protocol. * See http://tools.ietf.org/html/rfc4566.html#page-39 * Also see http://www.regular-expressions.info/posixbrackets.html for some help on regexes. */ class SdpParser extends RegexParsers { def nonWsString: Parser[String] = """\p{Graph}+""".r def number: Parser[Long] = """\p{Digit}+""".r ^^ { _.toLong } def text: Parser[String] = """\p{Print}+""".r def username: Parser[Username] = ("-" | nonWsString) ^^ { case "-" => UserIdsNotSupported case s: String => UserLogin(s) } def netType: Parser[String] = "IN" def addrType: Parser[String] = "IP4|IP6".r def media: Parser[Media] = ("video" | "audio" | "text" | "application") ^^ { case "video" => Video case "audio" => Audio case "text" => Text case "application" => Application } val token = """( \x21 | [\x23-\x27]|[\x2A-\x2B]|[\x2D-\x2E]|[\x30-\x39]|[\x41-\x5A]|[\x5E-\x7E])+""".r def protocolVersionField: Parser[ProtocolVersion] = "v=" ~ number ^^ { case _ ~ n => ProtocolVersion(n) } def originField: Parser[Origin] = ("o=" ~ username ~ number ~ number ~ netType ~ addrType ~ opt(nonWsString)) ^^ { case _ ~ username ~ sessionId ~ sessionVersion ~ netType ~ addrType ~ unicastAddress => Origin(username, sessionId, sessionVersion, netType, addrType, unicastAddress) } def sessionNameField: Parser[SessionName] = ("s=" ~ text) ^^ { case _ ~ text => SessionName(text) } def informationField: Parser[Information] = ("i=" ~ text) ^^ { case _ ~ text => Information(text) } def uriField : Parser[URI] = ("u=" ~ text) ^^ { case _ ~ s => new URI(s)} def protocol : Parser[Protocol] = ("""RTP/AVP""" | "udp") ^^ { s => Protocol(s) } def mediaField : Parser[MediaField] = ("m=" ~ media ~ number ~ protocol ~ number) ^^ { case _ ~ media ~ port ~ protocol ~ payloadType => MediaField(media, port, protocol, payloadType) } def keyValueAttribute : Parser[Attribute]= ( token ~ ":" ~ nonWsString ) ^^ { case key ~ ":" ~ value => KeyValueAttribute(key, value)} def binaryAttribute: Parser[Attribute]= nonWsString ^^ { s => BinaryAttribute(s)} def attributeField : Parser[List[Attribute]] = "a=" ~ rep(keyValueAttribute | binaryAttribute) ^^ { case _ ~ l => l } def mediaDescriptions: Parser[Any] = rep(mediaField ~ rep (attributeField)) } object SdpParser extends App{ val src = """ m=audio 49170 RTP/AVP 0 m=audio 49170 RTP/AVP 0 a=rtpmap:99 h263-1998/90000""" val parser = new SdpParser val parseResult = parser.parseAll(parser.mediaDescriptions, src) println (parseResult) }