char/live.umm.gay
git clone https://git.t4t.associates/char/live.umm.gay
bcf019e
main
1use std:: sync:: Arc ; 2 3use axum::{ 4 extract:: State , 5 http::{ header, HeaderMap , Method , StatusCode }, 6 response::{ IntoResponse , Response }, 7}; 8use miette::{ miette, Context , IntoDiagnostic , Result }; 9use tracing:: instrument; 10 11use crate ::{ 12 streams::{ get_ongoing_stream, OngoingStream }, 13 util:: log_http_error, 14AppState , 15}; 16 17use webrtc::{ 18 api:: API as WebRTC , 19 peer_connection::{ sdp:: session_description:: RTCSessionDescription , RTCPeerConnection }, 20 track:: track_local:: TrackLocalWriter , 21}; 22 23use super :: create_rtc_config; 24 25async fn setup_whip_connection ( 26channel : & str , 27webrtc : Arc < WebRTC >, 28) ->Result < Arc < RTCPeerConnection >> { 29let rtc_config =create_rtc_config (); 30 31let peer_connection =Arc :: new ( 32 webrtc 33. new_peer_connection ( rtc_config) 34. await 35. into_diagnostic () ?, 36); 37 38let OngoingStream { 39 video_track, 40 audio_track, 41 .. 42} =get_ongoing_stream ( channel). await ?; 43 44 peer_connection. on_track ( Box :: new ( move |track, _recv, _tx|{ 45let local_track =if track. codec (). capability . mime_type . starts_with ( "audio/" ) { 46& audio_track 47} else { 48& video_track 49} 50. clone (); 51 52 tokio:: spawn ( async move { 53let mut rtp_buf =vec! [ 0u8 ; 1500 ]; 54while let Ok (( rtp_read, _)) = track. read ( & mut rtp_buf). await { 55let ( Ok ( _) |Err ( webrtc:: Error :: ErrClosedPipe )) = local_track. write ( & rtp_buf[ ..rtp_read]). await else { break }; 56} 57Result ::<()>:: Ok (()) 58}); 59 60Box :: pin ( async {}) 61})); 62 63Ok ( peer_connection) 64} 65 66# [ instrument ( skip_all )] 67# [ axum :: debug_handler ] 68pub async fn handle_whip ( 69method : Method , 70headers : HeaderMap , 71State ( app): State < AppState >, 72offer : String , 73) ->Response { 74if method !=Method :: POST { 75return ( StatusCode :: METHOD_NOT_ALLOWED , "Please use POST!" ). into_response (); 76} 77 78let Some ( auth) = headers. get ( header:: AUTHORIZATION ) else { return ( StatusCode :: UNAUTHORIZED , "Authorization was not set" ). into_response () }; 79let auth = auth 80. to_str () 81. into_diagnostic () 82. wrap_err ( "Failed to decode auth header" ) 83. unwrap (); 84let auth = auth. strip_prefix ( "Bearer " ). unwrap_or ( auth); 85 86let Some (( channel, key)) = auth. split_once ( ':' ) else { return ( StatusCode :: UNAUTHORIZED , "Invalid Authorization header" ). into_response () }; 87 88let mut db = app 89. db 90. acquire () 91. await 92. expect ( "SQLite connections never fail" ); 93match sqlx:: query!( 94"SELECT stream FROM streams WHERE stream = ? AND password = ?" , 95 channel, 96 key 97) 98. fetch_one ( & mut db) 99. await 100{ 101Err ( sqlx:: Error :: RowNotFound ) =>{ 102return log_http_error ( StatusCode :: UNAUTHORIZED , "Invalid stream key." ) 103} 104Err ( e) =>return log_http_error ( StatusCode :: INTERNAL_SERVER_ERROR , e), 105 _ =>{} 106}; 107 108let peer_connection =match setup_whip_connection ( channel, app. webrtc ) 109. await 110. wrap_err ( "Failed to initialize peer connection" ) 111{ 112Ok ( peer_connection) => peer_connection, 113Err ( e) =>{ 114return log_http_error ( StatusCode :: INTERNAL_SERVER_ERROR , e); 115} 116}; 117 118let Ok ( description) =RTCSessionDescription :: offer ( offer) else { return log_http_error ( StatusCode :: BAD_REQUEST , "Malformed SDP offer" ) }; 119if let Err ( e) = peer_connection. set_remote_description ( description). await { 120return log_http_error ( StatusCode :: INTERNAL_SERVER_ERROR , e); 121}; 122 123let mut gather_complete = peer_connection. gathering_complete_promise (). await ; 124 125let answer =match peer_connection. create_answer ( None ). await { 126Ok ( answer) => answer, 127Err ( e) =>return log_http_error ( StatusCode :: INTERNAL_SERVER_ERROR , e), 128}; 129 130if let Err ( e) = peer_connection. set_local_description ( answer). await { 131return log_http_error ( StatusCode :: INTERNAL_SERVER_ERROR , e); 132} 133 134let _ = gather_complete. recv (). await ; 135 136match peer_connection. local_description (). await { 137Some ( desc) =>( StatusCode :: CREATED , desc. sdp ). into_response (), 138None =>log_http_error ( 139StatusCode :: INTERNAL_SERVER_ERROR , 140miette! ( "No local description exists!" ), 141), 142} 143}