anchor.diff:  
   unparse(_) ->
       undefined.
  +
  +
  +%% @doc Add trickle ICE candidates to an SDP
  +-spec add_candidates(#sdp{}|binary(), candidate_map()) ->
  +    #sdp{} | {error, term()}.
  +
  +add_candidates(#sdp{medias=Medias}=SDP, Candidates) ->
  +    case add_candidates(Medias, 0, Candidates, []) of
  +        {ok, Medias2} ->
  +            SDP#sdp{medias=Medias2};
  +        {error, Error} ->
  +            {error, Error}
  +    end;
  +
  +add_candidates(SDP, Candidates) ->
  +    case parse(SDP) of
  +        #sdp{} = Parsed ->
  +            add_candidates(Parsed, Candidates);
  +        error ->
  +            {error, parse_error}
  +    end.
  +
  +
  +%% @private
  +add_candidates([], _Index, _Candidates, Acc) ->
  +    {ok, lists:reverse(Acc)};
  +
  +add_candidates([#sdp_m{attributes=Attrs}=Media|Rest], Index, Candidates, Acc) ->
  +    case lists:keyfind(<<"mid">>, 1, Attrs) of
  +        {_, [Mid]} ->
  +            List = maps:get({Mid, Index}, Candidates, []),
  +            Attrs2 = lists:foldr(
  +                fun(Line, FAcc) ->
  +                    case Line of
  +                        <<"candidate:", FRest/binary>> ->
  +                            Data = binary:split(FRest, <<" ">>, [global]),
  +                            [{<<"candidate">>, Data}|FAcc];
  +                        _ ->
  +                            lager:error("L: ~p", [Line]),
  +                            FAcc
  +                    end
  +                end,
  +                Attrs,
  +                List),
  +            Media2 = Media#sdp_m{attributes=Attrs2},
  +            add_candidates(Rest, Index+1, Candidates, [Media2|Acc]);
  +        _ ->
  +            {error, missing_mid_in_sdp}
  +    end.
  +
  +
  +%% @doc Extract trickle ICE candidates from an SDP
  +-spec extract_candidates(#sdp{}|binary()) ->
  +    candidate_map() | {error, term()}.
  +
  +extract_candidates(#sdp{medias=Medias}) ->
  +    extract_candidates(Medias, 0, #{});
  +
  +extract_candidates(SDP) ->
  +    case parse(SDP) of
  +        #sdp{} = Parsed ->
  +            extract_candidates(Parsed);
  +        error ->
  +            {error, parse_error}
  +    end.
  +
  +%% @private
  +extract_candidates([], _Index, Acc) ->
  +    Acc;
  +
  +extract_candidates([#sdp_m{attributes=Attrs}|Rest], Index, Acc) ->
  +    case lists:keyfind(<<"mid">>, 1, Attrs) of
  +        {_, [Mid]} ->
  +            List = proplists:get_all_values(<<"candidate">>, Attrs),
  +            Acc2 = lists:foldr(
  +                fun(AList, FAcc) ->
  +                    Data = nklib_util:bjoin(AList, <<" ">>),
  +                    Values1 = maps:get({Mid, Index}, FAcc, []),
  +                    Values2 = [<<"candidate:", Data/binary>>|Values1],
  +                    maps:put({Mid, Index}, Values2, FAcc)
  +                end,
  +                Acc,
  +                List),
  +            extract_candidates(Rest, Index+1, Acc2);
  +        _ ->
  +            {error, missing_mid_in_sdp}
  +    end.
  +
  +
  +
  +
  +
  +
  +
       
   
   
  
positive.code:     ?assertMatch(SDP, parse(Bin)).
  
  
  -endif.
  
  
  
negative.code:                         Attributes :: 
                              [{rtpmap, Pos::integer(), Data::binary()} | binary()]
                      }.
  
  -type candidate_map() ::
      #{
          {Mid::binary(), Index::integer()} => binary()
      }.
  
  
  
  
  %% ===================================================================
  