diff options
Diffstat (limited to 'lib/megaco/src/engine/megaco_sdp.erl')
-rw-r--r-- | lib/megaco/src/engine/megaco_sdp.erl | 1645 |
1 files changed, 1645 insertions, 0 deletions
diff --git a/lib/megaco/src/engine/megaco_sdp.erl b/lib/megaco/src/engine/megaco_sdp.erl new file mode 100644 index 0000000000..90911fe24a --- /dev/null +++ b/lib/megaco/src/engine/megaco_sdp.erl @@ -0,0 +1,1645 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2001-2009. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +%%---------------------------------------------------------------------- +%% Purpose: RFC 4566 +%%---------------------------------------------------------------------- + +-module(megaco_sdp). + +%%---------------------------------------------------------------------- +%% Include files +%%---------------------------------------------------------------------- + +-include_lib("megaco/include/megaco.hrl"). +-include_lib("megaco/src/app/megaco_internal.hrl"). +-include_lib("megaco/include/megaco_message_v1.hrl"). +-include_lib("megaco/include/megaco_sdp.hrl"). + + +%%---------------------------------------------------------------------- +%% External exports +%%---------------------------------------------------------------------- + +-export([ + decode/1, encode/1, + get_sdp_record_from_PropertyGroup/2 + ]). + + +%%---------------------------------------------------------------------- +%% Internal exports +%%---------------------------------------------------------------------- + + +%%---------------------------------------------------------------------- +%% Macros +%%---------------------------------------------------------------------- + + +%%---------------------------------------------------------------------- +%% Records +%%---------------------------------------------------------------------- + + +%%====================================================================== +%% External functions +%%====================================================================== + +%% --------------------------------------------------------------------- +%% decode(PP) -> {ok, SDP} | {error, Reason} +%% +%% This function performs the following conversion: +%% property_parm() -> sdp() +%% property_group() -> sdp_property_group() +%% property_groups() -> sdp_property_groups() +%% +%% --------------------------------------------------------------------- + +decode(SDP) -> + case (catch do_decode(SDP)) of + {ok, _} = OK -> + OK; + {error, _} = ERR -> + ERR; + {'EXIT', Reason} -> + {error, {exit, Reason}}; + CRAP -> + {error, {crap, CRAP}} + end. + +do_decode(PP) when is_record(PP, 'PropertyParm') -> + decode_PropertyParm(PP); +do_decode([PP|_] = PG) when is_record(PP, 'PropertyParm') -> + decode_PropertyGroup(PG); +do_decode([H|_] = PGs) when is_list(H) -> + decode_PropertyGroups(PGs); +do_decode(asn1_NOVALUE = V) -> + {ok, V}; +do_decode(Bad) -> + {error, {bad_sdp, Bad}}. + + +%% --------------------------------------------------------------------- +%% encode(SDPs) -> {ok, PP} | {error, Reason} +%% +%% This function performs the following conversion: +%% sdp() -> property_parm() +%% sdp_property_group() -> property_group() +%% sdp_property_groups() -> property_groups() +%% +%% --------------------------------------------------------------------- + +encode(SDP) -> + case (catch do_encode(SDP)) of + {ok, _} = OK -> + OK; + {error, _} = ERR -> + ERR; + {'EXIT', Reason} -> + {error, {exit, Reason}}; + CRAP -> + {error, {crap, CRAP}} + end. + +do_encode(SDP) when is_tuple(SDP) -> + {ok, encode_PropertyParm(SDP)}; +do_encode([SDP|_] = PG) when is_tuple(SDP) -> + encode_PropertyGroup(PG); +do_encode([H|_] = PGs) when is_list(H) -> + encode_PropertyGroups(PGs); +do_encode(asn1_NOVALUE = V) -> + {ok, V}. + + +%%----------------------------------------------------------------- +%% Generate sdp records from PropertyParm records +%%----------------------------------------------------------------- + +decode_PropertyGroups(PGs) -> + decode_PropertyGroups(PGs, []). + +decode_PropertyGroups([], DecodedPGs) -> + {ok, lists:reverse(DecodedPGs)}; + +decode_PropertyGroups([PG | PGs], DecodedPGs) -> + {ok, DecodedPG} = decode_PropertyGroup(PG), + decode_PropertyGroups(PGs, [DecodedPG | DecodedPGs]). + + +decode_PropertyGroup(PG) -> + {ok, decode_PropertyGroup(PG, [])}. + +decode_PropertyGroup([], Acc) -> + lists:reverse(Acc); + +decode_PropertyGroup([PP | PG], Acc) -> + case (catch decode_PropertyParm(PP)) of + {ok, PP2} -> + decode_PropertyGroup(PG, [PP2 | Acc]); + {error, Reason} -> + decode_PropertyGroup(PG, [{PP, Reason} | Acc]); + Error -> + decode_PropertyGroup(PG, [{PP, Error} | Acc]) + end. + + +%% ===== Protocol Version ===== +%% +decode_PropertyParm(#'PropertyParm'{name = "v", + value = [V], + extraInfo = asn1_NOVALUE}) -> + decode_pp_protocol_version(V); + + +%% ===== Origin ===== +%% +decode_PropertyParm(#'PropertyParm'{name = "o", + value = [V], + extraInfo = asn1_NOVALUE}) -> + decode_pp_origin(V); + + +%% ===== Session Name ===== +%% +decode_PropertyParm(#'PropertyParm'{name = "s", + value = [V], + extraInfo = asn1_NOVALUE}) -> + decode_pp_session_name(V); + + +%% ===== Session and Media Information ===== +%% +decode_PropertyParm(#'PropertyParm'{name = "i", + value = [V], + extraInfo = asn1_NOVALUE}) -> + decode_pp_session_media_id(V); + + +%% ===== URI ===== +%% +decode_PropertyParm(#'PropertyParm'{name = "u", + value = [V], + extraInfo = asn1_NOVALUE}) -> + decode_pp_uri(V); + + +%% ===== Email Address and Phone Number ===== +%% +decode_PropertyParm(#'PropertyParm'{name = "e", + value = [V], + extraInfo = asn1_NOVALUE}) -> + decode_pp_email(V); + +decode_PropertyParm(#'PropertyParm'{name = "p", + value = [V], + extraInfo = asn1_NOVALUE}) -> + decode_pp_phone(V); + + +%% ===== Connection Data ===== +%% +decode_PropertyParm(#'PropertyParm'{name = "c", + value = [V], + extraInfo = asn1_NOVALUE}) -> + decode_pp_connection_data(V); + + +%% ===== Bandwidth ===== +%% +decode_PropertyParm(#'PropertyParm'{name = "b", + value = [V], + extraInfo = asn1_NOVALUE}) -> + decode_pp_bandwidth(V); + + +%% ===== Times, Repeat Times and Time Zones ===== +%% +decode_PropertyParm(#'PropertyParm'{name = "t", + value = [V], + extraInfo = asn1_NOVALUE }) -> + decode_pp_times(V); +decode_PropertyParm(#'PropertyParm'{name = "r", + value = [V], + extraInfo = asn1_NOVALUE}) -> + decode_pp_rtimes(V); +decode_PropertyParm(#'PropertyParm'{name = "z", + value = [V], + extraInfo = asn1_NOVALUE}) -> + decode_pp_tzones(V); + + +%% ===== Encryption Keys ===== +%% +decode_PropertyParm(#'PropertyParm'{name = "k", + value = [V], + extraInfo = asn1_NOVALUE}) -> + decode_pp_encryption_keys(V); + + +%% ===== Attributes ===== +%% +decode_PropertyParm(#'PropertyParm'{name = "a", + value = [V], + extraInfo = asn1_NOVALUE}) -> + decode_pp_attribute(V); + + +%% ===== Media Announcements ===== +%% +decode_PropertyParm(#'PropertyParm'{name = "m", + value = [V], + extraInfo = asn1_NOVALUE}) -> + decode_pp_media_announcement(V); + + +decode_PropertyParm(_PP) -> + ?d("decode_PropertyParm -> entry with" + "~n _PP: ~p", [_PP]), + {error, undefined_PropertyParm}. + + +%%----------------------------------------------------------------- +%% Generate PropertyParm records from sdp records +%%----------------------------------------------------------------- + +encode_PropertyGroups(PGs) -> + encode_PropertyGroups(PGs, []). + + +encode_PropertyGroups([], Acc) -> + {ok, lists:reverse(Acc)}; +encode_PropertyGroups([PG | PGs], Acc) -> + {ok, EncodedPG} = encode_PropertyGroup(PG), + encode_PropertyGroups(PGs, [EncodedPG | Acc]). + + +encode_PropertyGroup(PG) -> + encode_PropertyGroup(PG, []). + +encode_PropertyGroup([], Acc) -> + {ok, lists:reverse(Acc)}; +encode_PropertyGroup([PP | PG], Acc) -> + EncodedPP = encode_PropertyParm(PP), + encode_PropertyGroup(PG, [EncodedPP | Acc]). + + +%% ===== Protocol Version ===== +%% +encode_PropertyParm(#megaco_sdp_v{version = Version}) -> + encode_pp_protocol_version(Version); + + +%% ===== Origin ===== +%% +encode_PropertyParm(#megaco_sdp_o{user_name = User, + session_id = SessionId, + version = Version, + network_type = Network, + address_type = AddrType, + address = Addr}) -> + encode_pp_origin(User, SessionId, Version, Network, AddrType, Addr); + + +%% ===== Session Name ===== +%% +encode_PropertyParm(#megaco_sdp_s{name = Name}) -> + encode_pp_session_name(Name); + + +%% ===== Session and Media Information ===== +%% +encode_PropertyParm(#megaco_sdp_i{session_descriptor = SD}) -> + encode_pp_session_media_id(SD); + + +%% ===== URI ===== +%% +encode_PropertyParm(#megaco_sdp_u{uri = URI}) -> + encode_pp_uri(URI); + + +%% ===== Email Address and Phone Number ===== +%% +encode_PropertyParm(#megaco_sdp_e{email = Email}) -> + encode_pp_email(Email); + +encode_PropertyParm(#megaco_sdp_p{phone_number = Num}) -> + encode_pp_phone(Num); + + +%% ===== Connection Data ===== +%% +encode_PropertyParm(#megaco_sdp_c{network_type = NetType, + address_type = AddressType, + connection_addr = ConnectionAddr}) -> + encode_pp_connection_data(NetType, AddressType, ConnectionAddr); + + +%% ===== Bandwidth ===== +%% +encode_PropertyParm(#megaco_sdp_b{bwtype = BwType, + bandwidth = Bandwidth}) -> + encode_pp_bandwidth(BwType, Bandwidth); + + +%% ===== Times, Repeat Times and Time Zones ===== +%% +encode_PropertyParm(#megaco_sdp_t{start = Start, stop = Stop}) -> + encode_pp_times(Start, Stop); + +encode_PropertyParm(#megaco_sdp_r{repeat_interval = Repeat, + active_duration = Duration, + list_of_offsets = ListOfOffsets}) -> + encode_pp_rtimes(Repeat, Duration, ListOfOffsets); + +encode_PropertyParm(#megaco_sdp_z{list_of_adjustments = LOA}) -> + encode_pp_tzones(LOA); + + +%% ===== Encryption Keys ===== +%% +encode_PropertyParm(#megaco_sdp_k{method = Method, + encryption_key = EncryptionKey}) -> + encode_pp_encryption_keys(Method, EncryptionKey); + + +%% ===== Attributes ===== +%% +encode_PropertyParm(#megaco_sdp_a_cat{category = Category}) -> + encode_pp_attribute_cat(Category); + +encode_PropertyParm(#megaco_sdp_a_keywds{keywords = Keywords}) -> + encode_pp_attribute_keywds(Keywords); + +encode_PropertyParm(#megaco_sdp_a_tool{name_and_version = NameAndVersion}) -> + encode_pp_attribute_tool(NameAndVersion); + +encode_PropertyParm(#megaco_sdp_a_ptime{packet_time = PacketTime}) -> + encode_pp_attribute_ptime(PacketTime); + +encode_PropertyParm( + #megaco_sdp_a_maxptime{maximum_packet_time = MaxPacketTime}) -> + encode_pp_attribute_maxptime(MaxPacketTime); + +encode_PropertyParm(#megaco_sdp_a_rtpmap{payload_type = Payload, + encoding_name = EncName, + clock_rate = ClockRate, + encoding_parms = EncPar}) -> + encode_pp_attribute_rtpmap(Payload, EncName, ClockRate, EncPar); + +encode_PropertyParm(#megaco_sdp_a_orient{orientation = Orientation}) -> + encode_pp_attribute_orient(Orientation); + +encode_PropertyParm(#megaco_sdp_a_type{conf_type = CType}) -> + encode_pp_attribute_type(CType); + +encode_PropertyParm(#megaco_sdp_a_charset{char_set = CharSet}) -> + encode_pp_attribute_charset(CharSet); + +encode_PropertyParm(#megaco_sdp_a_sdplang{tag = Tag}) -> + encode_pp_attribute_sdplang(Tag); + +encode_PropertyParm(#megaco_sdp_a_lang{tag = Tag}) -> + encode_pp_attribute_lang(Tag); + +encode_PropertyParm(#megaco_sdp_a_framerate{frame_rate = FrameRate}) -> + encode_pp_attribute_framerate(FrameRate); + +encode_PropertyParm(#megaco_sdp_a_quality{quality = Quality}) -> + encode_pp_attribute_quality(Quality); + +encode_PropertyParm(#megaco_sdp_a_fmtp{format = Fmt, param = Params}) -> + encode_pp_attribute_fmtp(Fmt, Params); + +encode_PropertyParm(#megaco_sdp_a{attribute = Attr, value = Value}) -> + encode_pp_attribute(Attr, Value); + + +%% ===== Media Announcements ===== +%% +encode_PropertyParm(#megaco_sdp_m{media = Media, + port = Port, + num_ports = NOP, + transport = Transport, + fmt_list = FMT}) -> + encode_pp_media_announcement(Media, Port, NOP, Transport, FMT); + + +%% This is a "manually" encoded PropertyParm, leave it as is. +%% +encode_PropertyParm(PP) when is_record(PP, 'PropertyParm') -> + PP; + + +%% Bad data +encode_PropertyParm(SDP) -> + error({unknown_sdp, SDP}). + + +%%----------------------------------------------------------------- +%% Func: get_sdp_record_from_PropertGroup/2 +%% Description: Get all sdp records of a certain type from a +%% property group +%%----------------------------------------------------------------- + +get_sdp_record_from_PropertyGroup(Type, PG) + when is_atom(Type) and is_list(PG) -> + F = fun(R) -> not is_pg_record(Type, R) end, + lists:filter(F, PG). + +is_pg_record(v, R) when is_record(R, megaco_sdp_v) -> true; +is_pg_record(c, R) when is_record(R, megaco_sdp_c) -> true; +is_pg_record(m, R) when is_record(R, megaco_sdp_m) -> true; +is_pg_record(o, R) when is_record(R, megaco_sdp_o) -> true; +is_pg_record(a, R) when is_record(R, megaco_sdp_a) -> true; +is_pg_record(a, R) when is_record(R, megaco_sdp_a_ptime) -> true; +is_pg_record(a, R) when is_record(R, megaco_sdp_a_rtpmap) -> true; +is_pg_record(b, R) when is_record(R, megaco_sdp_b) -> true; +is_pg_record(t, R) when is_record(R, megaco_sdp_t) -> true; +is_pg_record(r, R) when is_record(R, megaco_sdp_r) -> true; +is_pg_record(z, R) when is_record(R, megaco_sdp_z) -> true; +is_pg_record(k, R) when is_record(R, megaco_sdp_k) -> true; +is_pg_record(s, R) when is_record(R, megaco_sdp_s) -> true; +is_pg_record(i, R) when is_record(R, megaco_sdp_i) -> true; +is_pg_record(u, R) when is_record(R, megaco_sdp_u) -> true; +is_pg_record(e, R) when is_record(R, megaco_sdp_e) -> true; +is_pg_record(p, R) when is_record(R, megaco_sdp_p) -> true; +is_pg_record(_, _) -> false. + + +%%====================================================================== +%% Internal functions +%%====================================================================== + +%% ===== Protocol Version ===== +%% +decode_pp_protocol_version(Value) when is_list(Value) -> + ?d("decode_pp_protocol_version -> entry with" + "~n Value: ~p", [Value]), + Version = s2i(Value, invalid_protocol_version), + ?d("decode_pp_protocol_version -> entry with" + "~n Version: ~w", [Version]), + decode_pp_protocol_version(Version); +decode_pp_protocol_version(Version) when is_integer(Version) -> + ?d("decode_pp_protocol_version -> entry with" + "~n Version: ~w", [Version]), + {ok, #megaco_sdp_v{version = Version}}. + +encode_pp_protocol_version(Version) when is_integer(Version) -> + ?d("encode_pp_protocol_version -> entry with" + "~n Version: ~w", [Version]), + #'PropertyParm'{name = "v", + value = [integer_to_list(Version)]}; +encode_pp_protocol_version(Version) -> + error({invalid_protocol_version, Version}). + + +%% ===== Origin ===== +%% +decode_pp_origin(Value) -> + ?d("decode_pp_origin -> entry with" + "~n Value: ~p", [Value]), + case string:tokens(Value, " \t") of + [User, SessId, SessVersion, NetType, AddrType, UnicastAddress] -> + ?d("decode_pp_origin -> entry with" + "~n User: ~p" + "~n SessionId: ~p" + "~n Version: ~p" + "~n NetType: ~p" + "~n AddrType: ~p" + "~n UnicastAddress: ~p", + [User, SessId, SessVersion, NetType, AddrType, UnicastAddress]), + F = fun(X, R) -> + case (catch list_to_integer(X)) of + I when is_integer(I) -> + I; + _ -> + error({invalid_origin, {R, X}}) + end + end, + SID = F(SessId, sess_id), + V = F(SessVersion, sess_version), + SDP = #megaco_sdp_o{user_name = User, + session_id = SID, + version = V, + network_type = decode_network_type(NetType), + address_type = decode_address_type(AddrType), + address = UnicastAddress}, + {ok, SDP}; + Err -> + ?d("decode_pp_origin -> " + "~n Err: ~p", [Err]), + invalid_pp(origin, Value, Err) + end. + +encode_pp_origin(User0, SessionId0, SessVersion0, + NetType0, AddrType0, UnicastAddr0) -> + ?d("do_encode_pp_origin -> entry with" + "~n User0: ~p" + "~n SessionId0: ~p" + "~n SessVersion0: ~p" + "~n NetType0: ~p" + "~n AddrType0: ~p" + "~n UnicastAddr0: ~p", + [User0, SessionId0, SessVersion0, NetType0, AddrType0, UnicastAddr0]), + User = encode_origin_user(User0), + SessionId = encode_origin_session_id(SessionId0), + SessVersion = encode_origin_sess_version(SessVersion0), + NetType = encode_origin_network_type(NetType0), + AddrType = encode_origin_address_type(AddrType0), + UnicastAddr = encode_origin_unicast_address(UnicastAddr0), + #'PropertyParm'{name = "o", + value = [ + User ++ " " ++ + SessionId ++ " " ++ + SessVersion ++ " " ++ + NetType ++ " " ++ + AddrType ++ " " ++ + UnicastAddr + ]}. + +encode_origin_user(User) when is_list(User) -> + User; +encode_origin_user(BadUser) -> + error({invalid_origin_user, BadUser}). + +encode_origin_session_id(SID) when is_integer(SID) -> + integer_to_list(SID); +encode_origin_session_id(BadSID) -> + error({invalid_origin_session_id, BadSID}). + +encode_origin_sess_version(SessVersion) when is_integer(SessVersion) -> + integer_to_list(SessVersion); +encode_origin_sess_version(BadSessVersion) -> + error({invalid_origin_sess_version, BadSessVersion}). + +encode_origin_network_type(NT) -> + case (catch encode_network_type(NT)) of + {error, _} -> + error({invalid_origin_network_type, NT}); + Val -> + Val + end. + +encode_origin_address_type(AT) -> + case (catch encode_address_type(AT)) of + {error, _} -> + error({invalid_origin_address_type, AT}); + Val -> + Val + end. + +encode_origin_unicast_address(UnicastAddr) when is_list(UnicastAddr) -> + UnicastAddr; +encode_origin_unicast_address(BadUnicastAddr) -> + error({invalid_origin_unicast_address, BadUnicastAddr}). + + +%% ===== Session Name ===== +%% +decode_pp_session_name(Value) -> + ?d("decode_pp_session_name -> entry with" + "~n Value: ~p", [Value]), + {ok, #megaco_sdp_s{name = Value}}. + +encode_pp_session_name(Name) when is_list(Name) -> + ?d("encode_pp_session_name -> entry with" + "~n Name: '~s'", [Name]), + #'PropertyParm'{name = "s", + value = [Name]}; +encode_pp_session_name(BadName) -> + error({invalid_session_name, BadName}). + + +%% ===== Session and Media Information ===== +%% +decode_pp_session_media_id(Value) -> + ?d("decode_pp_session_media_id -> entry with" + "~n Value: ~p", [Value]), + {ok, #megaco_sdp_i{session_descriptor = Value}}. + +encode_pp_session_media_id(SD) when is_list(SD) -> + ?d("encode_pp_session_media_id -> entry with" + "~n SD: '~s'", [SD]), + #'PropertyParm'{name = "i", + value = [SD]}; +encode_pp_session_media_id(BadSD) -> + error({invalid_session_media_id, BadSD}). + + +%% ===== URI ===== +%% +decode_pp_uri(Value) -> + ?d("decode_pp_uri -> entry with" + "~n Value: ~p", [Value]), + {ok, #megaco_sdp_u{uri = Value}}. + +encode_pp_uri(URI) when is_list(URI) -> + ?d("encode_pp_uri -> entry with" + "~n URI: ~p", [URI]), + #'PropertyParm'{name = "u", + value = [URI]}; +encode_pp_uri(BadUri) -> + error({invalid_uri, BadUri}). + + +%% ===== Email Address and Phone Number ===== +%% +decode_pp_email(Value) -> + ?d("decode_pp_email -> entry with" + "~n Value: ~p", [Value]), + {ok, #megaco_sdp_e{email = Value}}. + +encode_pp_email(Email) when is_list(Email) -> + ?d("encode_pp_email -> entry with" + "~n Email: ~p", [Email]), + #'PropertyParm'{name = "e", + value = [Email]}; +encode_pp_email(BadEmail) -> + error({invalid_email, BadEmail}). + +decode_pp_phone(Value) -> + ?d("decode_pp_phone -> entry with" + "~n Value: ~p", [Value]), + {ok, #megaco_sdp_p{phone_number = Value}}. + +encode_pp_phone(Phone) when is_list(Phone) -> + ?d("encode_pp_phone -> entry with" + "~n Phone: ~p", [Phone]), + #'PropertyParm'{name = "p", + value = [Phone]}; +encode_pp_phone(BadPhone) -> + error({invalid_phone, BadPhone}). + + +%% ===== Connection Data ===== +%% +decode_pp_connection_data(Value) -> + ?d("decode_pp_connection_data -> entry with" + "~n Value: ~p", [Value]), + case string:tokens(Value, " \t") of + [NetType, AddrType, ConnectionAddr] -> + ?d("decode_pp_connection_data -> " + "~n NetType: ~p" + "~n AddrType: ~p" + "~n ConnectionAddr: ~p", [NetType, AddrType, ConnectionAddr]), + NT = decode_network_type(NetType), + AT = decode_address_type(AddrType), + case AT of + ip4 -> + ?d("decode_pp_connection_data -> ip4", []), + ConnAddr = + case string:tokens(ConnectionAddr, "\/") of + [Base, TtlStr, NumOfAddrs] -> + ?d("decode_pp_connection_data -> " + "~n Base: ~p" + "~n TtlStr: ~p" + "~n NumOfAddrs:~p", + [Base, TtlStr, NumOfAddrs]), + TTL = s2i(TtlStr, + invalid_connection_data_ttl), + ?d("decode_pp_connection_data -> TTL: ~p", + [TTL]), + NOA = + s2i(NumOfAddrs, + invalid_connection_data_conn_addr_num_of), + ?d("decode_pp_connection_data -> NOA: ~p", + [NOA]), + #megaco_sdp_c_conn_addr{base = Base, + ttl = TTL, + num_of = NOA}; + [Base, TtlStr] -> + ?d("decode_pp_connection_data -> " + "~n Base: ~p" + "~n TtlStr: ~p", + [Base, TtlStr]), + TTL = + s2i(TtlStr, + invalid_connection_data_conn_addr_ttl), + ?d("decode_pp_connection_data -> TTL: ~p", + [TTL]), + #megaco_sdp_c_conn_addr{base = Base, + ttl = TTL}; + [Base] -> + Base + end, + ?d("decode_pp_connection_data -> " + "~n ConnAddr: ~p", [ConnAddr]), + SDP = #megaco_sdp_c{network_type = NT, + address_type = AT, + connection_addr = ConnAddr}, + {ok, SDP}; + ip6 -> + ?d("decode_pp_connection_data -> ip6", []), + SDP = #megaco_sdp_c{network_type = NT, + address_type = AT, + connection_addr = ConnectionAddr}, + {ok, SDP}; + _ -> + SDP = #megaco_sdp_c{network_type = NT, + address_type = AT, + connection_addr = ConnectionAddr}, + {ok, SDP} + end; + Err -> + invalid_pp(connection_data, Value, Err) + end. + +encode_pp_connection_data(NetType0, AddrType0, ConnAddr0) -> + ?d("encode_pp_connection_data -> entry with" + "~n NetType0: ~p" + "~n AddrType0: ~p" + "~n ConnAddr0: ~p", [NetType0, AddrType0, ConnAddr0]), + NetType = encode_conn_data_network_type(NetType0), + AddrType = encode_conn_data_address_type(AddrType0), + ConnAddr = encode_conn_data_conn_addr(AddrType0, ConnAddr0), + Val = NetType ++ " " ++ AddrType ++ " " ++ ConnAddr, + #'PropertyParm'{name = "c", + value = [Val]}. + +encode_conn_data_network_type(NT) -> + case (catch encode_network_type(NT)) of + {error, _} -> + error({invalid_connection_data_network_type, NT}); + Val -> + Val + end. + +encode_conn_data_address_type(AT) -> + case (catch encode_address_type(AT)) of + {error, _} -> + error({invalid_connection_data_address_type, AT}); + Val -> + Val + end. + +encode_conn_data_conn_addr(_, CA) when is_list(CA) -> + CA; +encode_conn_data_conn_addr(ip4, CA) + when is_record(CA, megaco_sdp_c_conn_addr) -> + encode_conn_data_conn_addr(CA); +encode_conn_data_conn_addr(AT, CA) + when is_list(AT) and is_record(CA, megaco_sdp_c_conn_addr) -> + case tolower(AT) of + "ip4" -> + encode_conn_data_conn_addr(CA); + _ -> + error({invalid_connection_data_conn_addr, {AT, CA}}) + end; +encode_conn_data_conn_addr(_, BadCA) -> + error({invalid_connection_data_conn_addr, BadCA}). + +encode_conn_data_conn_addr(#megaco_sdp_c_conn_addr{base = Base0, + ttl = TTL0, + num_of = undefined}) -> + Base = encode_conn_data_conn_addr_base(Base0), + TTL = encode_conn_data_conn_addr_ttl(TTL0), + Base ++ "/" ++ TTL; +encode_conn_data_conn_addr(#megaco_sdp_c_conn_addr{base = Base0, + ttl = TTL0, + num_of = NumOf0}) -> + Base = encode_conn_data_conn_addr_base(Base0), + TTL = encode_conn_data_conn_addr_ttl(TTL0), + NumOf = encode_conn_data_conn_addr_num_of(NumOf0), + Base ++ "/" ++ TTL ++ "/" ++ NumOf. + +encode_conn_data_conn_addr_base(Base) when is_list(Base) -> + Base; +encode_conn_data_conn_addr_base(BadBase) -> + error({invalid_connection_data_conn_addr_base, BadBase}). + +encode_conn_data_conn_addr_ttl(TTL) when is_integer(TTL) -> + integer_to_list(TTL); +encode_conn_data_conn_addr_ttl(BadTTL) -> + error({invalid_connection_data_conn_addr_ttl, BadTTL}). + +encode_conn_data_conn_addr_num_of(NumOf) when is_integer(NumOf) -> + integer_to_list(NumOf); +encode_conn_data_conn_addr_num_of(BadNumOf) -> + error({invalid_connection_data_conn_addr_num_of, BadNumOf}). + + +%% ===== Bandwidth ===== +%% +decode_pp_bandwidth(Value) -> + ?d("decode_pp_bandwidth -> entry with" + "~n Value: ~p", [Value]), + case string:tokens(Value, ":") of + [BwTypeStr, BandwidthStr] -> + ?d("decode_pp_bandwidth -> " + "~n BwTypeStr: ~p" + "~n BandwidthStr: ~p", [BwTypeStr, BandwidthStr]), + BwType = decode_bandwidth_bwt(BwTypeStr), + ?d("decode_pp_bandwidth -> " + "~n BwType: ~w", [BwType]), + Bandwidth = decode_bandwidth_bw(BandwidthStr), + ?d("decode_pp_bandwidth -> " + "~n Bandwidth: ~w", [Bandwidth]), + SDP = #megaco_sdp_b{bwtype = BwType, + bandwidth = Bandwidth}, + {ok, SDP}; + Err -> + invalid_pp(bandwidth_info, Value, Err) + end. + +encode_pp_bandwidth(BwType0, Bandwidth0) -> + ?d("encode_pp_bandwidth -> entry with" + "~n BwType0: ~p" + "~n Bandwidth0: ~p", [BwType0, Bandwidth0]), + BwType = encode_bandwidth_bwt(BwType0), + Bandwidth = encode_bandwidth_bw(Bandwidth0), + Val = BwType ++ ":" ++ Bandwidth, + #'PropertyParm'{name = "b", + value = [Val]}. + +decode_bandwidth_bwt("CT") -> + ct; +decode_bandwidth_bwt("AS") -> + as; +decode_bandwidth_bwt(BwType) when is_list(BwType) -> + BwType; +decode_bandwidth_bwt(BadBwType) -> + error({invalid_bandwidth_bwtype, BadBwType}). + +encode_bandwidth_bwt(ct) -> + "CT"; +encode_bandwidth_bwt(as) -> + "AS"; +encode_bandwidth_bwt(BwType) when is_list(BwType) -> + BwType; +encode_bandwidth_bwt(BadBwType) -> + error({invalid_bandwidth_bwtype, BadBwType}). + +decode_bandwidth_bw(Bandwidth) -> + s2i(Bandwidth, invalid_bandwidth_bandwidth). + +encode_bandwidth_bw(Bandwidth) when is_integer(Bandwidth) -> + integer_to_list(Bandwidth); +encode_bandwidth_bw(BadBandwidth) -> + error({invalid_bandwidth_bandwidth, BadBandwidth}). + + +%% ===== Times ===== +%% +decode_pp_times(Value) -> + ?d("decode_pp_times -> entry with" + "~n Value: ~p", [Value]), + case string:tokens(Value, " \t") of + [StartStr, StopStr] -> + ?d("decode_pp_times -> " + "~n StartStr: ~p" + "~n StopStr: ~p", [StartStr, StopStr]), + Start = decode_times_start(StartStr), + ?d("decode_pp_times -> entry with" + "~n Stop: ~w", [Start]), + Stop = decode_times_stop(StopStr), + ?d("decode_pp_times -> entry with" + "~n Stop: ~w", [Stop]), + SDP = #megaco_sdp_t{start = Start, + stop = Stop}, + {ok, SDP}; + Err -> + invalid_pp(times, Value, Err) + end. + +encode_pp_times(Start0, Stop0) -> + ?d("encode_pp_times -> entry with" + "~n Start0: ~p" + "~n Stop0: ~p", [Start0, Stop0]), + Start = encode_times_start(Start0), + Stop = encode_times_stop(Stop0), + Val = Start ++ " " ++ Stop, + #'PropertyParm'{name = "t", + value = [Val]}. + +decode_times_start(Time) -> + s2i(Time, invalid_times_start). + +encode_times_start(Time) when is_integer(Time) -> + integer_to_list(Time); +encode_times_start(BadTime) -> + error({invalid_times_start, BadTime}). + +decode_times_stop(Time) -> + s2i(Time, invalid_times_stop). + +encode_times_stop(Time) when is_integer(Time) -> + integer_to_list(Time); +encode_times_stop(BadTime) -> + error({invalid_times_stop, BadTime}). + + +%% ===== Repeat Times ===== +%% +decode_pp_rtimes(Value) -> + ?d("decode_pp_rtimes -> entry with" + "~n Value: ~p", [Value]), + case string:tokens(Value, " \t") of + [Repeat, Duration | ListOfOffsets] -> + ?d("decode_pp_rtimes -> " + "~n Repeat: ~p" + "~n Duration: ~p" + "~n ListOfOffsets: ~p", [Repeat, Duration, ListOfOffsets]), + SDP = #megaco_sdp_r{repeat_interval = Repeat, + active_duration = Duration, + list_of_offsets = ListOfOffsets}, + {ok, SDP}; + Err -> + invalid_pp(repeat_times, Value, Err) + end. + +encode_pp_rtimes(Repeat0, Duration0, ListOfOffsets0) -> + ?d("encode_pp_rtimes -> entry with" + "~n Repeat0: ~p" + "~n Duration0: ~p" + "~n ListOfOffsets0: ~p", [Repeat0, Duration0, ListOfOffsets0]), + Repeat = encode_rtimes_repeat(Repeat0), + Duration = encode_rtimes_duration(Duration0), + ListOfOffsets = encode_rtimes_list_of_offsets(ListOfOffsets0), + Val = Repeat ++ " " ++ Duration ++ ListOfOffsets, + #'PropertyParm'{name = "r", + value = [Val]}. + +encode_rtimes_repeat(Repeat) when is_list(Repeat) -> + Repeat; +encode_rtimes_repeat(BadRepeat) -> + error({invalid_rtimes_repeat, BadRepeat}). + +encode_rtimes_duration(Duration) when is_list(Duration) -> + Duration; +encode_rtimes_duration(BadDuration) -> + error({invalid_rtimes_duration, BadDuration}). + +encode_rtimes_list_of_offsets(LOO) when is_list(LOO) -> + F = fun(Off, Acc) when is_list(Off) -> + Acc ++ " " ++ Off; + (BadOff, Acc) -> + error({invalid_rtimes_list_of_offsets, {BadOff, Acc}}) + end, + lists:foldl(F, [], LOO); +encode_rtimes_list_of_offsets(BadLoo) -> + error({invalid_rtimes_list_of_offsets, BadLoo}). + + +%% ===== Time Zones ===== +%% +decode_pp_tzones(Value) when is_list(Value) and (length(Value) > 0) -> + ?d("decode_pp_ztimes -> entry with" + "~n Value: ~p", [Value]), + LOA = decode_tzones_list_of_adjustments(string:tokens(Value, " \t"), []), + {ok, #megaco_sdp_z{list_of_adjustments = LOA}}; +decode_pp_tzones(BadValue) -> + error({invalid_tzones_list_of_adjustments, BadValue}). + +encode_pp_tzones(LOA) -> + ?d("encode_pp_ztimes -> entry with" + "~n LOA: ~p", [LOA]), + Val = encode_tzones_list_of_adjustments(LOA), + #'PropertyParm'{name = "z", + value = [Val]}. + +decode_tzones_list_of_adjustments([], Acc) -> + lists:reverse(Acc); +decode_tzones_list_of_adjustments([Adj], Acc) -> + error({invalid_tzones_list_of_adjustments, Adj, lists:reverse(Acc)}); +decode_tzones_list_of_adjustments([Time, Offset | LOA], Acc) -> + Adj = #megaco_sdp_z_adjustement{time = Time, offset = Offset}, + decode_tzones_list_of_adjustments(LOA, [Adj | Acc]). + +encode_tzones_list_of_adjustments([H|_] = LOA) + when is_record(H, megaco_sdp_z_adjustement) -> + F = fun(#megaco_sdp_z_adjustement{time = T, offset = O}, Acc) -> + Acc ++ " " ++ T ++ " " ++ O; + (BadAdjustment, Acc) -> + error({invalid_tzones_list_of_adjustments, + {BadAdjustment, Acc}}) + end, + lists:foldl(F, [], LOA); +encode_tzones_list_of_adjustments(LOA) -> + error({invalid_tzones_list_of_adjustments, LOA}). + + +%% ===== Encryption Keys ===== +%% +decode_pp_encryption_keys(Value) -> + ?d("decode_pp_encryption_keys -> entry with" + "~n Value: ~p", [Value]), + {M, E} = + case string:tokens(Value, ":") of + [Method, EncryptionKey] -> + ?d("decode_pp_encryption_keys -> " + "~n Method: ~p" + "~n EncryptionKey: ~p", [Method, EncryptionKey]), + {Method, EncryptionKey}; + [Method] -> + ?d("decode_pp_encryption_keys -> " + "~n Method: ~p", [Method]), + {Method, undefined}; + Err -> + invalid_pp(encryption_key, Value, Err) + end, + M2 = + case tolower(M) of + "clear" -> + clear; + "base64" -> + base64; + "uri" -> + uri; + "prompt" -> + prompt; + _ -> + M + end, + ?d("decode_pp_encryption_keys -> " + "~n M2: ~p", [M2]), + SDP = #megaco_sdp_k{method = M2, + encryption_key = E}, + {ok, SDP}. + +encode_pp_encryption_keys(prompt = _Method, undefined) -> + ?d("encode_pp_encryption_keys(prompt) -> entry", []), + #'PropertyParm'{name = "k", + value = ["prompt"]}; +encode_pp_encryption_keys(clear = _Method, EncryptionKey) + when is_list(EncryptionKey) -> + ?d("encode_pp_encryption_keys(clear) -> entry with" + "~n EncryptionKey: ~p", [EncryptionKey]), + #'PropertyParm'{name = "k", + value = ["clear:" ++ EncryptionKey]}; +encode_pp_encryption_keys(base64 = _Method, EncryptionKey) + when is_list(EncryptionKey) -> + ?d("encode_pp_encryption_keys(base64) -> entry with" + "~n EncryptionKey: ~p", [EncryptionKey]), + #'PropertyParm'{name = "k", + value = ["base64:" ++ EncryptionKey]}; +encode_pp_encryption_keys(uri = _Method, EncryptionKey) + when is_list(EncryptionKey) -> + ?d("encode_pp_encryption_keys(uri) -> entry with" + "~n EncryptionKey: ~p", [EncryptionKey]), + #'PropertyParm'{name = "k", + value = ["uri:" ++ EncryptionKey]}; +encode_pp_encryption_keys(Method, EncryptionKey) + when is_list(Method) and is_list(EncryptionKey) -> + ?d("encode_pp_encryption_keys -> entry with" + "~n Method: ~p" + "~n EncryptionKey: ~p", [Method, EncryptionKey]), + #'PropertyParm'{name = "k", + value = [Method ++ ":" ++ EncryptionKey]}; +encode_pp_encryption_keys(BadMethod, BadEK) -> + error({invalid_encryption_keys, {BadMethod, BadEK}}). + + +%% ===== Attributes ===== +%% +decode_pp_attribute(Value) -> + ?d("decode_pp_attribute -> entry with" + "~n Value: ~p", [Value]), + First = string:chr(Value, $:), + if + (First > 0) and (First < length(Value)) -> + ?d("decode_pp_attribute -> value attribute", []), + Attr = string:substr(Value, 1, First -1), + AttrValue = string:substr(Value, First + 1), + ?d("decode_pp_attribute -> " + "~n Attr: ~p" + "~n AttrValue: ~p", [Attr, AttrValue]), + decode_pp_attribute_value(Attr, AttrValue); + + First > 0 -> + ?d("decode_pp_attribute -> value attribute (empty)", []), + Attr = string:substr(Value, 1, First -1), + ?d("decode_pp_attribute -> " + "~n Attr: ~p", [Attr]), + decode_pp_attribute_value(Attr, []); + + true -> + ?d("decode_pp_attribute -> binary attribute", []), + {ok, #megaco_sdp_a{attribute = Value}} + + end. + +decode_pp_attribute_value("cat", AttrValue) -> + ?d("decode_pp_attribute -> cat", []), + SDP = #megaco_sdp_a_cat{category = AttrValue}, + {ok, SDP}; + +decode_pp_attribute_value("keywds", AttrValue) -> + ?d("decode_pp_attribute -> keywds", []), + SDP = #megaco_sdp_a_keywds{keywords = AttrValue}, + {ok, SDP}; + +decode_pp_attribute_value("tool", AttrValue) -> + ?d("decode_pp_attribute -> tool", []), + SDP = #megaco_sdp_a_tool{name_and_version = AttrValue}, + {ok, SDP}; + +decode_pp_attribute_value("ptime", AttrValue) -> + ?d("decode_pp_attribute -> ptime", []), + PacketTimeStr = string:strip(AttrValue, both, $ ), + PacketTime = + s2i(PacketTimeStr, invalid_ptime_packet_time), + ?d("decode_pp_attribute -> PacketTime: ~w", [PacketTime]), + SDP = #megaco_sdp_a_ptime{packet_time = PacketTime}, + {ok, SDP}; + +decode_pp_attribute_value("maxptime", AttrValue) -> + ?d("decode_pp_attribute -> maxptime", []), + MaxPacketTimeStr = string:strip(AttrValue, both, $ ), + MaxPacketTime = + s2i(MaxPacketTimeStr, invalid_maxptime_maximum_packet_time), + ?d("decode_pp_attribute -> MaxPacketTime: ~w", [MaxPacketTime]), + SDP = #megaco_sdp_a_maxptime{maximum_packet_time = MaxPacketTime}, + {ok, SDP}; + +decode_pp_attribute_value("rtpmap", AttrValue) -> + ?d("decode_pp_attribute -> rtpmap", []), + case string:tokens(AttrValue, "\/ \t") of + [PayloadStr, EncName, ClockRateStr | EncPar] -> + ?d("decode_pp_attribute -> " + "~n PayloadStr: ~p" + "~n EncName: ~p" + "~n ClockRateStr: ~p" + "~n EncPar: ~p", + [PayloadStr, EncName, ClockRateStr, EncPar]), + Payload = + s2i(PayloadStr, invalid_rtpmap_payload), + ?d("decode_pp_attribute -> Payload: ~w", + [Payload]), + ClockRate = + s2i(ClockRateStr, invalid_rtpmap_payload), + ?d("decode_pp_attribute -> ClockRate: ~w", + [ClockRate]), + SDP = + #megaco_sdp_a_rtpmap{payload_type = Payload, + encoding_name = EncName, + clock_rate = ClockRate, + encoding_parms = EncPar}, + {ok, SDP}; + _ -> + error({invalid_rtpmap, AttrValue}) + end; + +decode_pp_attribute_value("orient", AttrValue) -> + ?d("decode_pp_attribute -> orient", []), + Orientation = decode_attribute_orientation(AttrValue), + SDP = #megaco_sdp_a_orient{orientation = Orientation}, + {ok, SDP}; + +decode_pp_attribute_value("type", AttrValue) -> + ?d("decode_pp_attribute -> type", []), + SDP = #megaco_sdp_a_type{conf_type = AttrValue}, + {ok, SDP}; + +decode_pp_attribute_value("charset", AttrValue) -> + ?d("decode_pp_attribute -> charset", []), + SDP = #megaco_sdp_a_charset{char_set = AttrValue}, + {ok, SDP}; + +decode_pp_attribute_value("sdplang", AttrValue) -> + ?d("decode_pp_attribute -> sdplang", []), + SDP = #megaco_sdp_a_sdplang{tag = AttrValue}, + {ok, SDP}; + +decode_pp_attribute_value("lang", AttrValue) -> + ?d("decode_pp_attribute -> lang", []), + SDP = #megaco_sdp_a_lang{tag = AttrValue}, + {ok, SDP}; + +decode_pp_attribute_value("framerate", AttrValue) -> + ?d("decode_pp_attribute -> framerate", []), + SDP = #megaco_sdp_a_framerate{frame_rate = AttrValue}, + {ok, SDP}; + +decode_pp_attribute_value("quality", AttrValue) -> + ?d("decode_pp_attribute -> quality", []), + QualityStr = AttrValue, + Quality = s2i(QualityStr, invalid_quality_quality), + ?d("decode_pp_attribute -> Quality: ~w", [Quality]), + SDP = #megaco_sdp_a_quality{quality = Quality}, + {ok, SDP}; + +decode_pp_attribute_value("fmtp", AttrValue) -> + ?d("decode_pp_attribute -> fmtp", []), + FMTP = AttrValue, + First = string:chr(FMTP, $ ), + if + (First > 0) and (First < length(FMTP)) -> + ?d("decode_pp_attribute_value -> valid fmtp with params", []), + Format = string:substr(FMTP, 1, First - 1), + Params = string:substr(FMTP, First + 1), + ?d("decode_pp_attribute_value -> " + "~n Format: ~p" + "~n Params: ~p", [Format, Params]), + SDP = #megaco_sdp_a_fmtp{format = Format, + param = Params}, + {ok, SDP}; + + First > 0 -> + ?d("decode_pp_attribute -> valid fmtp", []), + Format = string:substr(FMTP, 1, First - 1), + ?d("decode_pp_attribute -> " + "~n Format: ~p", [Format]), + {ok, #megaco_sdp_a_fmtp{format = Format, param = []}}; + + true -> + ?d("decode_pp_attribute_value -> no params", []), + {ok, #megaco_sdp_a_fmtp{format = FMTP, param = []}} + + end; + +decode_pp_attribute_value(Attr, AttrValue) -> + ?d("decode_pp_attribute -> unknown value attribute", []), + {ok, #megaco_sdp_a{attribute = Attr, value = AttrValue}}. + +decode_attribute_orientation("portrait") -> + portrait; +decode_attribute_orientation("landscape") -> + landscape; +decode_attribute_orientation("seascape") -> + seascape; +decode_attribute_orientation(BadOrientation) -> + error({invalid_orient_orientation, BadOrientation}). + +encode_attribute_orientation(portrait) -> + "portrait"; +encode_attribute_orientation(landscape) -> + "landscape"; +encode_attribute_orientation(seascape) -> + "seascape"; +encode_attribute_orientation(BadOrientation) -> + error({invalid_orient_orientation, BadOrientation}). + + +encode_pp_attribute_cat(Cat) when is_list(Cat) -> + ?d("encode_pp_attribute_cat -> entry with" + "~n Cat: ~p", [Cat]), + #'PropertyParm'{name = "a", + value = ["cat:" ++ Cat]}; +encode_pp_attribute_cat(BadCat) -> + error({invalid_cat_category, BadCat}). + + +encode_pp_attribute_keywds(Keywords) when is_list(Keywords) -> + ?d("encode_pp_attribute_keywds -> entry with" + "~n Keywords: ~p", [Keywords]), + #'PropertyParm'{name = "a", + value = ["keywds:" ++ Keywords]}; +encode_pp_attribute_keywds(BadKeywords) -> + error({invalid_keywds_keywords, BadKeywords}). + + +encode_pp_attribute_tool(NameAndVersion) when is_list(NameAndVersion) -> + ?d("encode_pp_attribute_tool -> entry with" + "~n NameAndVersion: ~p", [NameAndVersion]), + #'PropertyParm'{name = "a", + value = ["tool:" ++ NameAndVersion]}; +encode_pp_attribute_tool(BadNameAndVersion) -> + error({invalid_tool_name_and_version, BadNameAndVersion}). + + +encode_pp_attribute_ptime(PacketTime) when is_integer(PacketTime) -> + ?d("encode_pp_attribute_ptime -> entry with" + "~n PacketTime: ~w", [PacketTime]), + #'PropertyParm'{name = "a", + value = ["ptime:" ++ integer_to_list(PacketTime)]}; +encode_pp_attribute_ptime(BadPT) -> + error({invalid_ptime_packet_time, BadPT}). + + +encode_pp_attribute_maxptime(MaxPacketTime) when is_integer(MaxPacketTime) -> + ?d("encode_pp_attribute_maxptime -> entry with" + "~n MaxPacketTime: ~w", [MaxPacketTime]), + #'PropertyParm'{name = "a", + value = ["maxptime:" ++ integer_to_list(MaxPacketTime)]}; +encode_pp_attribute_maxptime(BadMPT) -> + error({invalid_maxptime_maximum_packet_time, BadMPT}). + + +encode_pp_attribute_rtpmap(Payload0, EncName0, ClockRate0, EncPar0) -> + ?d("encode_pp_attribute_rtpmap -> entry with" + "~n Payload0: ~p" + "~n EncName0: ~p" + "~n ClockRate0: ~p" + "~n EncPar0: ~p", [Payload0, EncName0, ClockRate0, EncPar0]), + Payload = encode_rtpmap_payload(Payload0), + EncName = encode_rtpmap_encoding_name(EncName0), + ClockRate = encode_rtpmap_clockrate(ClockRate0), + EncPar = encode_rtpmap_encoding_parms(EncPar0), + Val = "rtpmap:" ++ Payload ++ " " ++ + EncName ++ "/" ++ ClockRate ++ EncPar, + #'PropertyParm'{name = "a", + value = [Val]}. + +encode_rtpmap_payload(Payload) when is_integer(Payload) -> + integer_to_list(Payload); +encode_rtpmap_payload(BadPayload) -> + error({invalid_rtpmap_payload, BadPayload}). + +encode_rtpmap_encoding_name(EncName) when is_list(EncName) -> + EncName; +encode_rtpmap_encoding_name(BadEncName) -> + error({invalid_rtpmap_encoding_name, BadEncName}). + +encode_rtpmap_clockrate(ClockRate) when is_integer(ClockRate) -> + integer_to_list(ClockRate); +encode_rtpmap_clockrate(BadClockRate) -> + error({invalid_rtpmap_clockrate, BadClockRate}). + +encode_rtpmap_encoding_parms(EncPar) when is_list(EncPar) -> + F = fun(EP, Acc) when is_list(EP) -> + Acc ++ "/" ++ EP; + (BadEP, Acc) -> + error({invalid_rtpmap_encoding_parms, {BadEP, Acc}}) + end, + lists:foldl(F, [], EncPar); +encode_rtpmap_encoding_parms(BadEncPar) -> + error({invalid_rtpmap_encoding_parms, BadEncPar}). + + +encode_pp_attribute_orient(Orientation0) -> + ?d("encode_pp_attribute_orient -> entry with" + "~n Orientation0: ~w", [Orientation0]), + Orientation = encode_attribute_orientation(Orientation0), + #'PropertyParm'{name = "a", + value = ["orient:" ++ Orientation]}. + + +encode_pp_attribute_type(CType) when is_list(CType) -> + ?d("encode_pp_attribute_type -> entry with" + "~n CType: ~p", [CType]), + #'PropertyParm'{name = "a", + value = ["type:" ++ CType]}; +encode_pp_attribute_type(BadCType) -> + error({invalid_type_conf_type, BadCType}). + + +encode_pp_attribute_charset(CharSet) when is_list(CharSet) -> + ?d("encode_pp_attribute_charset -> entry with" + "~n CharSet: ~p", [CharSet]), + #'PropertyParm'{name = "a", + value = ["charset:" ++ CharSet]}; +encode_pp_attribute_charset(BadCharSet) -> + error({invalid_charset_char_set, BadCharSet}). + + +encode_pp_attribute_sdplang(SdpLang) when is_list(SdpLang) -> + ?d("encode_pp_attribute_sdplang -> entry with" + "~n SdpLang: ~p", [SdpLang]), + #'PropertyParm'{name = "a", + value = ["sdplang:" ++ SdpLang]}; +encode_pp_attribute_sdplang(BadSdpLang) -> + error({invalid_sdplang_tag, BadSdpLang}). + + +encode_pp_attribute_lang(Lang) when is_list(Lang) -> + ?d("encode_pp_attribute_lang -> entry with" + "~n Lang: ~p", [Lang]), + #'PropertyParm'{name = "a", + value = ["lang:" ++ Lang]}; +encode_pp_attribute_lang(BadLang) -> + error({invalid_lang_tag, BadLang}). + + +encode_pp_attribute_framerate(FrameRate) when is_list(FrameRate) -> + ?d("encode_pp_attribute_framerate -> entry with" + "~n FrameRate: ~p", [FrameRate]), + #'PropertyParm'{name = "a", + value = ["framerate:" ++ FrameRate]}; +encode_pp_attribute_framerate(BadFrameRate) -> + error({invalid_framerate_frame_rate, BadFrameRate}). + + +encode_pp_attribute_quality(Quality) when is_integer(Quality) -> + ?d("encode_pp_attribute_quality -> entry with" + "~n Quality: ~w", [Quality]), + #'PropertyParm'{name = "a", + value = ["quality:" ++ integer_to_list(Quality)]}; +encode_pp_attribute_quality(BadQ) -> + error({invalid_quality_quality, BadQ}). + + +encode_pp_attribute_fmtp(Fmt0, Params0) -> + ?d("encode_pp_attribute_rtpmap -> entry with" + "~n Fmt0: ~p" + "~n Params0: ~p", [Fmt0, Params0]), + Fmt = encode_fmtp_format(Fmt0), + Params = encode_fmtp_param(Params0), + Val = "fmtp:" ++ Fmt ++ " " ++ Params, + #'PropertyParm'{name = "a", + value = [Val]}. + +encode_fmtp_format(Fmt) when is_list(Fmt) -> + Fmt; +encode_fmtp_format(BadFmt) -> + error({invalid_fmtp_format, BadFmt}). + +encode_fmtp_param(Param) when is_list(Param) -> + Param; +encode_fmtp_param(BadParam) -> + error({invalid_fmtp_param, BadParam}). + + +encode_pp_attribute(Attr, undefined) when is_list(Attr) -> + ?d("encode_pp_attribute_rtpmap -> entry with" + "~n Attr: ~p", [Attr]), + #'PropertyParm'{name = "a", + value = [Attr]}; +encode_pp_attribute(Attr, Value) when is_list(Attr) and is_list(Value) -> + ?d("encode_pp_attribute_rtpmap -> entry with" + "~n Attr: ~p" + "~n Value: ~p", [Attr, Value]), + #'PropertyParm'{name = "a", + value = [Attr ++ ":" ++ Value]}; +encode_pp_attribute(BadAttr, BadAttrValue) -> + error({invalid_attribute, {BadAttr, BadAttrValue}}). + + +%% ===== Media Announcements ===== +%% +decode_pp_media_announcement(Value) -> + case string:tokens(Value, " \t") of + [Media0, PortInfo, Transport | FMT] -> + Media = + case tolower(Media0) of + "audio" -> audio; + "video" -> video; + "application" -> application; + "data" -> data; + "control" -> control; + _ -> Media0 + end, + {Port, NoOfPorts} = + case string:tokens(PortInfo, "/") of + [P1, NP] -> + {s2i(P1, invalid_media_announcement_port), + s2i(NP, invalid_media_announcement_nof_ports)}; + [P2] -> + {s2i(P2, invalid_media_announcement_port), + undefined}; + Err -> + invalid_pp(mnta_port_info, Value, Err) + end, + SDP = #megaco_sdp_m{media = Media, + port = Port, + num_ports = NoOfPorts, + transport = Transport, + fmt_list = FMT}, + {ok, SDP}; + Err -> + invalid_pp(media_name_transp_addr, Value, Err) + end. + + +encode_pp_media_announcement(Media0, Port0, undefined, Transport0, FMT0) -> + ?d("encode_pp_media_announcement -> entry with" + "~n Media0: ~p" + "~n Port0: ~p" + "~n Transport0: ~p" + "~n FMT0: ~p", [Media0, Port0, Transport0, FMT0]), + Media = encode_media_announcement_media(Media0), + Port = encode_media_announcement_port(Port0), + Transport = encode_media_announcement_transport(Transport0), + FMT = encode_media_announcement_fmt_list(FMT0), + do_encode_pp_media_announcement(Media, Port, "", Transport, FMT); +encode_pp_media_announcement(Media0, Port0, NumPorts0, Transport0, FMT0) -> + ?d("encode_pp_media_announcement -> entry with" + "~n Media0: ~p" + "~n Port0: ~p" + "~n NumPorts0: ~p" + "~n Transport0: ~p" + "~n FMT0: ~p", [Media0, Port0, NumPorts0, Transport0, FMT0]), + Media = encode_media_announcement_media(Media0), + Port = encode_media_announcement_port(Port0), + NumPorts = encode_media_announcement_num_port(NumPorts0), + Transport = encode_media_announcement_transport(Transport0), + FMT = encode_media_announcement_fmt_list(FMT0), + do_encode_pp_media_announcement(Media, Port, NumPorts, Transport, FMT). + +do_encode_pp_media_announcement(Media, Port, NumOfPorts, Transport, FMT) -> + Val = Media ++ " " ++ Port ++ NumOfPorts ++ " " ++ Transport ++ FMT, + #'PropertyParm'{name = "m", + value = [Val]}. + +encode_media_announcement_media(Media) when is_atom(Media) -> + MaMedia = [audio, video, application, data, control], + case lists:member(Media, MaMedia) of + true -> + atom_to_list(Media); + false -> + error({invalid_media_announcement_media, Media}) + end; +encode_media_announcement_media(Media) when is_list(Media) -> + Media; +encode_media_announcement_media(BadMedia) -> + error({invalid_media_announcement_media, BadMedia}). + +encode_media_announcement_port(Port) when is_integer(Port) -> + integer_to_list(Port); +encode_media_announcement_port(BadPort) -> + error({invalid_media_announcement_port, BadPort}). + +encode_media_announcement_num_port(NumPort) when is_integer(NumPort) -> + "/" ++ integer_to_list(NumPort); +encode_media_announcement_num_port(BadNumPort) -> + error({invalid_media_announcement_num_port, BadNumPort}). + +encode_media_announcement_transport(Transport) when is_list(Transport) -> + Transport; +encode_media_announcement_transport(BadTransport) -> + error({invalid_media_announcement_transport, BadTransport}). + +encode_media_announcement_fmt_list(FmtList) when is_list(FmtList) -> + F = fun(FMT, Acc) when is_list(FMT) -> + Acc ++ " " ++ FMT; + (BadFMT, Acc) -> + error({invalid_media_announcement_fmt_list, {BadFMT, Acc}}) + end, + lists:foldl(F, [], FmtList); +encode_media_announcement_fmt_list(BadFmtList) -> + error({invalid_media_announcement_fmt_list, BadFmtList}). + + +decode_network_type(NT) when is_list(NT) -> + case tolower(NT) of + "in" -> in; + _ -> NT + end. + +encode_network_type(in) -> "IN"; +encode_network_type(NT) when is_list(NT) -> NT; +encode_network_type(Bad) -> + {error, {invalid_network_type, Bad}}. + + +decode_address_type(AT) when is_list(AT) -> + case tolower(AT) of + "ip4" -> ip4; + "ip6" -> ip6; + _ -> AT + end. + +encode_address_type(ip4) -> "IP4"; +encode_address_type(ip6) -> "IP6"; +encode_address_type(AT) when is_list(AT) -> + case toupper(AT) of + "IP4" -> "IP4"; + "IP6" -> "IP6"; + _ -> AT + end; +encode_address_type(Crap) -> + {error, {invalid_address_type, Crap}}. + + +s2i(S, E) -> + case (catch list_to_integer(S)) of + I when is_integer(I) -> + I; + _ -> + error({E, S}) + end. + +-define(LOWER(Char), + if + Char >= $A, Char =< $Z -> + Char - ($A - $a); + true -> + Char + end). +tolower(Chars) -> + [?LOWER(Char) || Char <- Chars]. + +-define(UPPER(Char), + if + Char >= $a, Char =< $z -> + Char + ($A - $a); + true -> + Char + end). +toupper(Chars) -> + [?UPPER(Char) || Char <- Chars]. + +invalid_pp(What, Value, Error) -> + {error, {invalid_PropertyParm, {What, Value, Error}}}. + +error(Reason) -> + throw({error, Reason}). + |