Tiberiu, I have written a very simple SDP parser attached, you can add more functions based on it.
Regards, kaiduan On Thu, Oct 15, 2009 at 6:14 PM, Costin-Tiberiu RADU < [email protected]> wrote: > Is it me, or there is nothing inside yxa to parse the SDP part of the > messages. > For example if I have an INVITE (which contains of course an SDP ) and I > want to do in server some processing to the SDP part, is there something > built in? > >From what I can see the SDP is part of the pachet is left unchanged and at > best the envelope (aka the headers) is changed. > Did I got it wrong or is it a way to parse the SDP ? > > Many thanks > > Regards > > Tiberiu > > > > În data de Jo, 01-10-2009 la 12:13 +0200, Fredrik Thulin a scris: > > On Thu, 2009-10-01 at 11:32 +0200, Costin-Tiberiu RADU wrote: > ... > > Is there something in the reverse direction? > > somefunct(#request{...}) -> Packet > > > > I was looking for something like this, but I could not find. > > Can somebody point me in the right direction? > > Follow the trail from transportlayer:send_proxy_request/4 > > /Fredrik > > > _______________________________________________ > Yxa-devel mailing > [email protected]https://lists.su.se/mailman/listinfo/yxa-devel > > > _______________________________________________ > Yxa-devel mailing list > [email protected] > https://lists.su.se/mailman/listinfo/yxa-devel > >
-module(sdp).
%-include_lib("eunit/include/eunit.hrl").
-export([parse_origin/1,
origin_to_string/1,
parse_session_name/1,
session_name_to_string/1,
parse_session_info/1,
session_info_to_string/1,
parse_uri/1,
uri_to_string/1,
parse_email/1,
email_to_string/1,
parse_phone_number/1,
phone_number_to_string/1,
parse_connection/1,
connection_to_string/1,
parse_bandwidth/1,
bandwidth_to_string/1,
parse_time/1,
time_to_string/1,
parse_repeat_time/1,
repeat_time_to_string/1,
parse_time_zone/1,
time_zone_to_string/1,
parse_encryption_key/1,
encryption_key_to_string/1,
parse_attribute/1,
attribute_to_string/1,
parse_media/1,
media_to_string/1,
parse_media_description/1,
parse_media_description/2,
media_description_to_string/1,
new_sdp/0,
parse/1,
sdp_to_string/1]).
%-export([add_attribute_to_sdp/3,
% remove_attribute_to_sdp/3,
% set_attribute_to_sdp/3,
% get_attribute_from_sdp/2,
% add_attribute_to_media_description/3,
% remove_attribute_from_media_description/3,
% set_attribute_to_media_description/2,
% get_attribute_from_media_description/2]).
%-export([test_parse_sdp/0,
%-export([test_parse_media_description/0]).
-define(CR, 16#0D).
-define(LF, 16#0A).
-define(SPACE, " ").
-include("sdp.hrl").
-include_lib("eunit/include/eunit.hrl").
extract(Bin, Offset) ->
extract3(Bin, Offset, []).
extract3(Bin, Offset, Acc) ->
case Bin of
<<_:Offset/binary, ?CR, ?LF, _/binary>> ->
{Offset + 2, lists:reverse(Acc)};
<<_:Offset/binary, ?LF, _/binary>> ->
{ok, Offset + 1, lists:reverse(Acc)};
<<_:Offset/binary, N:8, _/binary>> ->
extract3(Bin, Offset + 1, [N|Acc])
end.
parse_origin(Origin) ->
List = string:tokens(Origin, ?SPACE),
#origin{username = lists:nth(1, List),
id = lists:nth(2, List),
version = lists:nth(3, List),
nettype = lists:nth(4, List),
addrtype = lists:nth(5, List),
uaddress = lists:nth(6, List)}.
origin_to_string(O) when is_record(O, origin) ->
io_lib:format("o=~s ~s ~s ~s ~s ~s",[O#origin.username,
O#origin.id,
O#origin.version,
O#origin.nettype,
O#origin.addrtype,
O#origin.uaddress]).
parse_session_name(SessionName) ->
SessionName.
session_name_to_string(SN) ->
io_lib:format("s=~s", [SN]).
parse_session_info(SessionInfo) ->
SessionInfo.
session_info_to_string(SI) ->
io_lib:format("i=~s", [SI]).
parse_uri(Url) ->
Url.
uri_to_string(Uri) ->
io_lib:format("u=~s", [Uri]).
parse_email(Email) ->
Email.
email_to_string(E) ->
io_lib:format("e=~s", [E]).
parse_phone_number(PhoneNumber) ->
PhoneNumber.
phone_number_to_string(PN) ->
io_lib:format("p=~s", [PN]).
parse_connection(Connection) ->
List = string:tokens(Connection, ?SPACE),
#connection{nettype = lists:nth(1, List),
addrtype = lists:nth(2, List),
address = lists:nth(3, List)}.
connection_to_string(C) when is_record(C, connection) ->
io_lib:format("c=~s ~s ~s", [C#connection.nettype,
C#connection.addrtype,
C#connection.address]).
parse_bandwidth(Bandwidth) ->
Bandwidth.
bandwidth_to_string(B) ->
io_lib:format("b=~s", [B]).
parse_time(Time) ->
Time.
time_to_string(T) ->
io_lib:format("t=~s", [T]).
parse_repeat_time(Repeat) ->
Repeat.
repeat_time_to_string(RT) ->
io_lib:format("r=~s", [RT]).
parse_time_zone(TimeZone) ->
TimeZone.
time_zone_to_string(TZ) ->
io_lib:format("z=~s", [TZ]).
parse_encryption_key(Encryption) ->
Encryption.
encryption_key_to_string(E) ->
io_lib:format("k=~s", [E]).
parse_attribute(Attribute) ->
{Attr, Value } = parse_attribute4(Attribute, [], [], false),
#attribute{attribute = Attr, value = Value}.
parse_attribute4([], Attr, Value, true) ->
{lists:reverse(Attr), lists:reverse(Value)};
parse_attribute4([], Attr, Value, false) ->
{lists:reverse(Attr), Value};
parse_attribute4([$:|T], Attr, Value, false) ->
parse_attribute4(T, Attr, Value, true);
parse_attribute4([H|T], Attr, Value, false) ->
parse_attribute4(T, [H|Attr], Value, false);
parse_attribute4([H|T], Attr, Value, true) ->
parse_attribute4(T, Attr, [H|Value], true).
attribute_to_string(A) when is_record(A, attribute) ->
case A#attribute.value of
[] ->
io_lib:format("a=~s", [A#attribute.attribute]);
_ ->
io_lib:format("a=~s:~s", [A#attribute.attribute, A#attribute.value])
end.
parse_media(Media) ->
Lists = string:tokens(Media, ?SPACE),
#media{type = lists:nth(1, Lists),
port = lists:nth(2, Lists),
protocol = lists:nth(3, Lists),
formats = lists:nthtail(3, Lists)}.
media_to_string(MF) when is_record(MF, media) ->
io_lib:format("m=~s ~s ~s ~s", [MF#media.type,
MF#media.port,
MF#media.protocol,
MF#media.formats]).
parse_media_description(MD) when is_list(MD) ->
parse_media_description(list_to_binary(MD));
parse_media_description(Bin) ->
{_, MD } = parse_media_description(Bin, 0),
MD.
parse_media_description(Bin, Offset) ->
parse_media_description3(Bin, Offset, #media_description{}).
parse_media_description3(Bin, Offset, MD) when Offset == size(Bin) ->
NewAtts = lists:reverse(MD#media_description.attributes),
NewBds = lists:reverse(MD#media_description.bandwidths),
NewMD = MD#media_description{attributes = NewAtts,
bandwidths = NewBds},
{Offset, NewMD};
parse_media_description3(Bin, Offset, MD) ->
case Bin of
<<_:Offset/binary, $m, $=, _/binary>> ->
case MD#media_description.media of
undefined ->
{NewOffset, Media} = extract(Bin, Offset + 2),
MF = parse_media(Media),
NewMD = MD#media_description{media = MF},
parse_media_description3(Bin, NewOffset, NewMD);
_ ->
NewAtts = lists:reverse(MD#media_description.attributes),
NewBds = lists:reverse(MD#media_description.bandwidths),
NewMD = MD#media_description{attributes = NewAtts,
bandwidths = NewBds},
{Offset, NewMD}
end;
<<_:Offset/binary, $i, $=, _/binary>> ->
{NewOffset, Info} = extract(Bin, Offset + 2),
I = parse_session_info(Info),
NewMD = MD#media_description{info = I},
parse_media_description3(Bin, NewOffset, NewMD);
<<_:Offset/binary, $c, $=, _/binary>> ->
{NewOffset, Connection} = extract(Bin, Offset + 2),
C = parse_connection(Connection),
NewMD = MD#media_description{connection = C},
parse_media_description3(Bin, NewOffset, NewMD);
<<_:Offset/binary, $b, $=, _/binary>> ->
{NewOffset, Bandwidth} = extract(Bin, Offset + 2),
Bt = parse_bandwidth(Bandwidth),
Bts = [Bt | MD#media_description.bandwidths],
NewMD = MD#media_description{bandwidths = Bts},
parse_media_description3(Bin, NewOffset, NewMD);
<<_:Offset/binary, $k, $=, _/binary>> ->
{NewOffset, Key} = extract(Bin, Offset + 2),
K = parse_encryption_key(Key),
NewMD = MD#media_description{encryption_key = K},
parse_media_description3(Bin, NewOffset, NewMD);
<<_:Offset/binary, $a, $=, _/binary>> ->
{NewOffset, Attribute} = extract(Bin, Offset + 2),
At = parse_attribute(Attribute),
Ats = [At | MD#media_description.attributes],
NewMD = MD#media_description{attributes = Ats},
parse_media_description3(Bin, NewOffset, NewMD)
end.
media_description_to_string(MD) when is_record(MD, media_description) ->
R = [[media_to_string(MD#media_description.media), ?CR, ?LF]],
case MD#media_description.info of
undefined ->
R1 = R;
Info ->
R1 = [[Info, ?CR, ?LF]| R]
end,
case MD#media_description.connection of
undefined ->
R2 = R1;
C ->
R2 = [[connection_to_string(C), ?CR, ?LF]| R1]
end,
R3 = lists:foldl(fun(B, Result) ->
[[bandwidth_to_string(B), ?CR, ?LF]| Result] end,
R2, MD#media_description.bandwidths),
case MD#media_description.encryption_key of
undefined ->
R4 = R3;
Key ->
R4 = [[encryption_key_to_string(Key), ?CR, ?LF]| R3]
end,
Result = lists:foldl(fun(A, Result) ->
[[attribute_to_string(A), ?CR, ?LF]| Result] end,
R4, MD#media_description.attributes),
lists:flatten(lists:reverse(Result)).
%test_parse_media_description() ->
% ok.
new_sdp() ->
#sdp{}.
parse(Str) when is_list(Str)->
parse(list_to_binary(Str));
parse(Bin) when is_binary(Bin) ->
parse3(Bin, 0, #sdp{}).
parse3(Bin, Offset, Sdp) when Offset == size(Bin) ->
Sdp#sdp{bandwidths = lists:reverse(Sdp#sdp.bandwidths),
repeat_times = lists:reverse(Sdp#sdp.repeat_times),
attributes = lists:reverse(Sdp#sdp.attributes),
media_descriptions = lists:reverse(Sdp#sdp.media_descriptions)};
parse3(Bin, Offset, Sdp) ->
case Bin of
<<_:Offset/binary, $v, $=, _/binary>> ->
{NewOffset, Version} = extract(Bin, Offset + 2),
NewSdp = Sdp#sdp{version = list_to_integer(Version)},
parse3(Bin, NewOffset, NewSdp);
<<_:Offset/binary, $o, $=, _/binary>> ->
{NewOffset, Origin} = extract(Bin, Offset + 2),
NewSdp = Sdp#sdp{origin = parse_origin(Origin)},
parse3(Bin, NewOffset, NewSdp);
<<_:Offset/binary, $s, $=, _/binary>> ->
{NewOffset, SessionName} = extract(Bin, Offset + 2),
NewSdp = Sdp#sdp{session_name = parse_session_name(SessionName)},
parse3(Bin, NewOffset, NewSdp);
<<_:Offset/binary, $i, $=, _/binary>> ->
{NewOffset, SessionInfo} = extract(Bin, Offset + 2),
NewSdp = Sdp#sdp{session_info = parse_session_info(SessionInfo)},
parse3(Bin, NewOffset, NewSdp);
<<_:Offset/binary, $u, $=, _/binary>> ->
{NewOffset, Uri} = extract(Bin, Offset + 2),
NewSdp = Sdp#sdp{uri = parse_uri(Uri)},
parse3(Bin, NewOffset, NewSdp);
<<_:Offset/binary, $e, $=, _/binary>> ->
{NewOffset, Email} = extract(Bin, Offset + 2),
NewSdp = Sdp#sdp{email = parse_email(Email)},
parse3(Bin, NewOffset, NewSdp);
<<_:Offset/binary, $p, $=, _/binary>> ->
{NewOffset, PhoneNumber} = extract(Bin, Offset + 2),
NewSdp = Sdp#sdp{phone_number = parse_phone_number(PhoneNumber)},
parse3(Bin, NewOffset, NewSdp);
<<_:Offset/binary, $c, $=, _/binary>> ->
{NewOffset, Connection} = extract(Bin, Offset + 2),
NewSdp = Sdp#sdp{connection = parse_connection(Connection)},
parse3(Bin, NewOffset, NewSdp);
<<_:Offset/binary, $b, $=, _/binary>> ->
{NewOffset, Bandwidth} = extract(Bin, Offset + 2),
Bd = parse_bandwidth(Bandwidth),
Bds = [Bd | Sdp#sdp.bandwidths],
NewSdp = Sdp#sdp{bandwidths = Bds},
parse3(Bin, NewOffset, NewSdp);
<<_:Offset/binary, $t, $=, _/binary>> ->
{NewOffset, Time} = extract(Bin, Offset + 2),
NewSdp = Sdp#sdp{time = parse_time(Time)},
parse3(Bin, NewOffset, NewSdp);
<<_:Offset/binary, $r, $=, _/binary>> ->
{NewOffset, Repeat} = extract(Bin, Offset + 2),
Rt = parse_repeat_time(Repeat),
Rts = [Rt | Sdp#sdp.repeat_times],
NewSdp = Sdp#sdp{repeat_times = Rts},
parse3(Bin, NewOffset, NewSdp);
<<_:Offset/binary, $z, $=, _/binary>> ->
{NewOffset, TimeZone} = extract(Bin, Offset + 2),
NewSdp = Sdp#sdp{time_zone = parse_time_zone(TimeZone)},
parse3(Bin, NewOffset, NewSdp);
<<_:Offset/binary, $k, $=, _/binary>> ->
{NewOffset, Encryption} = extract(Bin, Offset +2),
NewSdp = Sdp#sdp{encryption_key = parse_encryption_key(Encryption)},
parse3(Bin, NewOffset, NewSdp);
<<_:Offset/binary, $a, $=, _/binary>> ->
{NewOffset, Attribute} = extract(Bin, Offset + 2),
At = parse_attribute(Attribute),
Ats = [At | Sdp#sdp.attributes],
NewSdp = Sdp#sdp{attributes = Ats},
parse3(Bin, NewOffset, NewSdp);
<<_:Offset/binary, $m, $=, _/binary>> ->
{NewOffset, MD} = parse_media_description(Bin, Offset),
MDs = [MD | Sdp#sdp.media_descriptions],
NewSdp = Sdp#sdp{media_descriptions = MDs},
parse3(Bin, NewOffset, NewSdp)
end.
sdp_to_string(S) when is_record(S, sdp) ->
R = [["v=0\r\n"]],
R1 = [[origin_to_string(S#sdp.origin), ?CR, ?LF] | R],
R2 = [[session_name_to_string(S#sdp.session_name), ?CR, ?LF] | R1],
case S#sdp.session_info of
undefined ->
R3 = R2;
Info ->
R3 = [[session_info_to_string(Info), ?CR, ?LF] | R2]
end,
case S#sdp.uri of
undefined ->
R4 = R3;
Uri ->
R4 = [[uri_to_string(Uri), ?CR, ?LF] | R3]
end,
case S#sdp.email of
undefined ->
R5 = R4;
Email ->
R5 = [[email_to_string(Email), ?CR, ?LF] | R4]
end,
case S#sdp.phone_number of
undefined ->
R6 = R5;
Phone ->
R6 = [[phone_number_to_string(Phone), ?CR, ?LF] | R5]
end,
case S#sdp.connection of
undefined ->
R7 = R6;
C ->
R7 = [[connection_to_string(C), ?CR, ?LF] | R6]
end,
R8 = lists:foldl(fun(B, Result) ->
[[bandwidth_to_string(B), ?CR, ?LF]| Result] end,
R7, S#sdp.bandwidths),
case S#sdp.time of
undefined ->
R9 = R8;
Time ->
R9 = [[time_to_string(Time), ?CR, ?LF] | R8]
end,
R10 = lists:foldl(fun(B, Result) ->
[[repeat_time_to_string(B), ?CR, ?LF]| Result] end,
R9, S#sdp.repeat_times),
case S#sdp.time_zone of
undefined ->
R11 = R10;
TZ ->
R11 = [[time_zone_to_string(TZ), ?CR, ?LF] | R10]
end,
case S#sdp.encryption_key of
undefined ->
R12 = R11;
EK ->
R12 = [[encryption_key_to_string(EK), ?CR, ?LF] | R11]
end,
R13 = lists:foldl(fun(A, Result) ->
[[attribute_to_string(A), ?CR, ?LF]| Result] end,
R12, S#sdp.attributes),
R14 = lists:foldl(fun(MD, Result) ->
[[media_description_to_string(MD)]| Result] end,
R13, S#sdp.media_descriptions),
lists:flatten(lists:reverse(R14)).
%% EUnit tests
parse_media_description_test() ->
MD = "m=video 51372 RTP/AVP 99\r\n"
"a=rtpmap:99 h263-1998/90000\r\n"
"a=sendrecv\r\n",
M = parse_media_description(MD),
io:format("M: ~p~n", [M]),
MD1 = media_description_to_string(M),
io:format("After parse, ~n~s", [MD1]),
?assertEqual(MD, MD1).
parse_sdp_test() ->
SDP = "v=0\r\n"
"o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5\r\n"
"s=SDP Seminar\r\n"
"i=A Seminar on the session description protocol\r\n"
"u=http://www.example.com/seminars/sdp.pdf\r\n"
"[email protected] (Jane Doe)\r\n"
"c=IN IP4 224.2.17.12/127\r\n"
"t=2873397496 2873404696\r\n"
"a=recvonly\r\n"
"m=audio 49170 RTP/AVP 0\r\n"
"m=video 51372 RTP/AVP 99\r\n"
"a=rtpmap:99 h263-1998/90000\r\n",
Sdp = parse(list_to_binary(SDP)),
io:format("Origin SDP: ~n~s", [SDP]),
SDP1 = sdp_to_string(Sdp),
io:format("Parsed SDP: ~n~s", [SDP1]),
?assertEqual(SDP, SDP1).
sdp.hrl
Description: Binary data
_______________________________________________ Yxa-devel mailing list [email protected] https://lists.su.se/mailman/listinfo/yxa-devel
