char/live.umm.gay
git clone https://git.t4t.associates/char/live.umm.gay
e154831
main
1use std:: sync::{ atomic:: Ordering , Arc }; 2 3use axum::{ 4 extract:: State , 5 http::{ header, HeaderMap , Method , StatusCode }, 6 response::{ IntoResponse , Response }, 7}; 8use miette::{ miette, Context , IntoDiagnostic , Result }; 9 10use crate ::{ 11 streams::{ get_ongoing_stream, OngoingStream }, 12 util:: log_http_error, 13AppState , 14}; 15 16use tracing:: instrument; 17use webrtc::{ 18 api:: API as WebRTC , 19 peer_connection::{ 20 configuration:: RTCConfiguration , peer_connection_state:: RTCPeerConnectionState , 21 sdp:: session_description:: RTCSessionDescription , RTCPeerConnection , 22}, 23}; 24 25async fn setup_whep_connection ( 26channel : & str , 27webrtc : Arc < WebRTC >, 28) ->Result < Arc < RTCPeerConnection >> { 29let rtc_config =RTCConfiguration :: default (); 30let peer_connection =Arc :: new ( 31 webrtc 32. new_peer_connection ( rtc_config) 33. await 34. into_diagnostic () ?, 35); 36 37let OngoingStream { 38 video_track, 39 audio_track, 40 viewer_count, 41} =get_ongoing_stream ( channel). await ?; 42 43 peer_connection. on_peer_connection_state_change ( Box :: new ( move |s : RTCPeerConnectionState |{ 44if s ==RTCPeerConnectionState :: Connected { 45 viewer_count. fetch_add ( 1 , Ordering :: Relaxed ); 46} 47 48if s ==RTCPeerConnectionState :: Disconnected { 49 viewer_count. fetch_sub ( 1 , Ordering :: Relaxed ); 50} 51 52Box :: pin ( async {}) 53})); 54 55 peer_connection 56. add_track ( video_track) 57. await 58. into_diagnostic () 59. wrap_err ( "Failed to add video track" ) ?; 60 61 peer_connection 62. add_track ( audio_track) 63. await 64. into_diagnostic () 65. wrap_err ( "Failed to add audio track" ) ?; 66 67Ok ( peer_connection) 68} 69 70# [ instrument ( skip_all )] 71pub async fn handle_whep ( 72method : Method , 73headers : HeaderMap , 74State ( app): State < AppState >, 75offer : String , 76) ->Response { 77if method !=Method :: POST { 78return ( StatusCode :: METHOD_NOT_ALLOWED , "Please use POST!" ). into_response (); 79} 80 81let channel =match headers 82. get ( header:: AUTHORIZATION ) 83. ok_or ( miette! ( "Authorization header was not set" )) 84. and_then ( |h|{ 85 h. to_str () 86. into_diagnostic () 87. wrap_err ( "Authorization header was malformed" ) 88}) { 89Ok ( a) => a, 90Err ( e) =>return log_http_error ( StatusCode :: BAD_REQUEST , e), 91}; 92let channel = channel. strip_prefix ( "Bearer " ). unwrap_or ( channel); 93 94let peer_connection =match setup_whep_connection ( channel, app. webrtc ). await { 95Ok ( p) => p, 96Err ( e) =>return log_http_error ( StatusCode :: INTERNAL_SERVER_ERROR , e), 97}; 98 99let Ok ( description) =RTCSessionDescription :: offer ( offer) else { return log_http_error ( StatusCode :: BAD_REQUEST , "Malformed SDP offer" ) }; 100if let Err ( e) = peer_connection. set_remote_description ( description). await { 101return log_http_error ( StatusCode :: INTERNAL_SERVER_ERROR , e); 102}; 103 104let mut gather_complete = peer_connection. gathering_complete_promise (). await ; 105 106let answer =match peer_connection. create_answer ( None ). await { 107Ok ( answer) => answer, 108Err ( e) =>return log_http_error ( StatusCode :: INTERNAL_SERVER_ERROR , e), 109}; 110 111if let Err ( e) = peer_connection. set_local_description ( answer). await { 112return log_http_error ( StatusCode :: INTERNAL_SERVER_ERROR , e); 113} 114 115let _ = gather_complete. recv (). await ; 116 117match peer_connection. local_description (). await { 118Some ( desc) =>( StatusCode :: CREATED , desc. sdp ). into_response (), 119None =>log_http_error ( 120StatusCode :: INTERNAL_SERVER_ERROR , 121miette! ( "No local description exists!" ), 122), 123} 124}