Matrix protocol in OCaml, Eio specialised
at main 193 lines 6.6 kB view raw
1(** VoIP call signaling operations. *) 2 3(** Call state *) 4type call_state = 5 | Ringing 6 | Connected 7 | Ended 8 9(** Generate a new call ID. *) 10let generate_call_id () = 11 (* Use transaction ID generator for call IDs *) 12 Matrix_proto.Id.Transaction_id.(generate () |> to_string) 13 14(** Generate a new party ID for multi-party calls. *) 15let generate_party_id () = 16 Matrix_proto.Id.Transaction_id.(generate () |> to_string) 17 18(** Send a call invite. 19 20 @param room_id The room to send the invite in 21 @param call_id The call ID 22 @param offer The SDP offer 23 @param lifetime How long the invite is valid in milliseconds 24 @param version Call version (0 or 1) 25 @param party_id Party ID for version 1 calls 26 @param invitee Optional user to invite (for version 1 calls) *) 27let send_invite client ~room_id ~call_id ~offer ~lifetime 28 ?(version = 1) ?party_id ?invitee () = 29 let room_id_str = Matrix_proto.Id.Room_id.to_string room_id in 30 let txn_id = Matrix_proto.Id.Transaction_id.(generate () |> to_string) in 31 let path = Printf.sprintf "/rooms/%s/send/m.call.invite/%s" 32 (Uri.pct_encode room_id_str) 33 (Uri.pct_encode txn_id) 34 in 35 let content : Matrix_proto.Event.Call_invite_content.t = { 36 call_id; 37 party_id; 38 version; 39 lifetime; 40 offer; 41 invitee; 42 } in 43 match Client.encode_body Matrix_proto.Event.Call_invite_content.jsont content with 44 | Error e -> Error e 45 | Ok body -> 46 match Client.put client ~path ~body () with 47 | Error e -> Error e 48 | Ok resp_body -> 49 match Client.decode_response Messages.send_response_jsont resp_body with 50 | Error e -> Error e 51 | Ok resp -> Ok resp.event_id 52 53(** Send call candidates (ICE candidates). 54 55 @param room_id The room 56 @param call_id The call ID 57 @param candidates List of ICE candidates 58 @param version Call version 59 @param party_id Party ID for version 1 calls *) 60let send_candidates client ~room_id ~call_id ~candidates 61 ?(version = 1) ?party_id () = 62 let room_id_str = Matrix_proto.Id.Room_id.to_string room_id in 63 let txn_id = Matrix_proto.Id.Transaction_id.(generate () |> to_string) in 64 let path = Printf.sprintf "/rooms/%s/send/m.call.candidates/%s" 65 (Uri.pct_encode room_id_str) 66 (Uri.pct_encode txn_id) 67 in 68 let content : Matrix_proto.Event.Call_candidates_content.t = { 69 call_id; 70 party_id; 71 version; 72 candidates; 73 } in 74 match Client.encode_body Matrix_proto.Event.Call_candidates_content.jsont content with 75 | Error e -> Error e 76 | Ok body -> 77 match Client.put client ~path ~body () with 78 | Error e -> Error e 79 | Ok resp_body -> 80 match Client.decode_response Messages.send_response_jsont resp_body with 81 | Error e -> Error e 82 | Ok resp -> Ok resp.event_id 83 84(** Send a call answer. 85 86 @param room_id The room 87 @param call_id The call ID 88 @param answer The SDP answer 89 @param version Call version 90 @param party_id Party ID for version 1 calls *) 91let send_answer client ~room_id ~call_id ~answer 92 ?(version = 1) ?party_id () = 93 let room_id_str = Matrix_proto.Id.Room_id.to_string room_id in 94 let txn_id = Matrix_proto.Id.Transaction_id.(generate () |> to_string) in 95 let path = Printf.sprintf "/rooms/%s/send/m.call.answer/%s" 96 (Uri.pct_encode room_id_str) 97 (Uri.pct_encode txn_id) 98 in 99 let content : Matrix_proto.Event.Call_answer_content.t = { 100 call_id; 101 party_id; 102 version; 103 answer; 104 } in 105 match Client.encode_body Matrix_proto.Event.Call_answer_content.jsont content with 106 | Error e -> Error e 107 | Ok body -> 108 match Client.put client ~path ~body () with 109 | Error e -> Error e 110 | Ok resp_body -> 111 match Client.decode_response Messages.send_response_jsont resp_body with 112 | Error e -> Error e 113 | Ok resp -> Ok resp.event_id 114 115(** Hang up a call. 116 117 @param room_id The room 118 @param call_id The call ID 119 @param reason Optional hangup reason 120 @param version Call version 121 @param party_id Party ID for version 1 calls *) 122let send_hangup client ~room_id ~call_id ?reason 123 ?(version = 1) ?party_id () = 124 let room_id_str = Matrix_proto.Id.Room_id.to_string room_id in 125 let txn_id = Matrix_proto.Id.Transaction_id.(generate () |> to_string) in 126 let path = Printf.sprintf "/rooms/%s/send/m.call.hangup/%s" 127 (Uri.pct_encode room_id_str) 128 (Uri.pct_encode txn_id) 129 in 130 let content : Matrix_proto.Event.Call_hangup_content.t = { 131 call_id; 132 party_id; 133 version; 134 reason; 135 } in 136 match Client.encode_body Matrix_proto.Event.Call_hangup_content.jsont content with 137 | Error e -> Error e 138 | Ok body -> 139 match Client.put client ~path ~body () with 140 | Error e -> Error e 141 | Ok resp_body -> 142 match Client.decode_response Messages.send_response_jsont resp_body with 143 | Error e -> Error e 144 | Ok resp -> Ok resp.event_id 145 146(** Reject an incoming call (same as hangup but with different semantics). *) 147let reject_call client ~room_id ~call_id ?(version = 1) ?party_id () = 148 let room_id_str = Matrix_proto.Id.Room_id.to_string room_id in 149 let txn_id = Matrix_proto.Id.Transaction_id.(generate () |> to_string) in 150 let path = Printf.sprintf "/rooms/%s/send/m.call.reject/%s" 151 (Uri.pct_encode room_id_str) 152 (Uri.pct_encode txn_id) 153 in 154 (* m.call.reject has the same structure as m.call.hangup *) 155 let content : Matrix_proto.Event.Call_hangup_content.t = { 156 call_id; 157 party_id; 158 version; 159 reason = None; 160 } in 161 match Client.encode_body Matrix_proto.Event.Call_hangup_content.jsont content with 162 | Error e -> Error e 163 | Ok body -> 164 match Client.put client ~path ~body () with 165 | Error e -> Error e 166 | Ok resp_body -> 167 match Client.decode_response Messages.send_response_jsont resp_body with 168 | Error e -> Error e 169 | Ok resp -> Ok resp.event_id 170 171(** TURN server credentials *) 172type turn_server = { 173 username : string; 174 password : string; 175 uris : string list; 176 ttl : int; 177} 178 179let turn_server_jsont = 180 Jsont.Object.( 181 map (fun username password uris ttl -> 182 { username; password; uris; ttl }) 183 |> mem "username" Jsont.string ~enc:(fun t -> t.username) 184 |> mem "password" Jsont.string ~enc:(fun t -> t.password) 185 |> mem "uris" (Jsont.list Jsont.string) ~dec_absent:[] ~enc:(fun t -> t.uris) 186 |> mem "ttl" Jsont.int ~enc:(fun t -> t.ttl) 187 |> finish) 188 189(** Get TURN server credentials from the homeserver. *) 190let get_turn_server client = 191 match Client.get client ~path:"/voip/turnServer" () with 192 | Error e -> Error e 193 | Ok body -> Client.decode_response turn_server_jsont body