anchor.diff:                          Attributes :: 
                               [{rtpmap, Pos::integer(), Data::binary()} | binary()]
                       }.
  +
  +-type candidate_map() ::
  +    #{
  +        {Mid::binary(), Index::integer()} => binary()
  +    }.
  +
  +
   
   
   %% ===================================================================
  
positive.code: 
  -export([new/2, new/0, empty/0, update/2, increment/1, parse/1, unparse/1]).
  -export([is_sdp/1, is_new/2, update_ip/2]).
  
  -export_type([sdp/0, sdp_a/0, sdp_m/0, sdp_t/0, address/0]).
  
  %% ===================================================================
  %% Types
  
negative.code: 
  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.
  
  
  
  
  
  
  
      
  
  
  