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 <
costin.tiberiu.r...@gmail.com> 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 
> listyxa-de...@lists.su.sehttps://lists.su.se/mailman/listinfo/yxa-devel
>
>
> _______________________________________________
> Yxa-devel mailing list
> Yxa-devel@lists.su.se
> 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";
          "e=j....@example.com (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).

Attachment: sdp.hrl
Description: Binary data

_______________________________________________
Yxa-devel mailing list
Yxa-devel@lists.su.se
https://lists.su.se/mailman/listinfo/yxa-devel

Reply via email to