@@ -2,19 +2,19 @@ use tokio::runtime::Runtime;
22use url:: Url ;
33
44use bytes:: Bytes ;
5- use hang:: catalog:: { Video , VideoConfig , H264 } ;
5+ use hang:: catalog:: { Audio , AudioConfig , Video , VideoConfig , AAC , H264 } ;
66use hang:: model:: { Frame , Timestamp , TrackProducer } ;
77use hang:: { Catalog , CatalogProducer } ;
88use moq_lite:: { BroadcastProducer , Track } ;
99use std:: ffi:: CStr ;
1010use std:: os:: raw:: c_char;
1111use std:: sync:: atomic:: { AtomicBool , Ordering } ;
12- use std:: sync:: { Mutex , OnceLock } ;
12+ use std:: sync:: Mutex ;
1313use std:: thread:: JoinHandle ;
1414use std:: { collections:: HashMap , time:: Duration } ;
1515
16- static IMPORT : OnceLock < Mutex < ImportJoy > > = OnceLock :: new ( ) ;
17- static HANDLE : OnceLock < Mutex < JoinHandle < ( ) > > > = OnceLock :: new ( ) ;
16+ static IMPORT : Mutex < Option < ImportJoy > > = Mutex :: new ( None ) ;
17+ static HANDLE : Mutex < Option < JoinHandle < ( ) > > > = Mutex :: new ( None ) ;
1818static RUNNING : AtomicBool = AtomicBool :: new ( false ) ;
1919
2020/// # Safety
@@ -63,12 +63,22 @@ pub unsafe extern "C" fn hang_start_from_c(
6363 } ) ;
6464 } ) ;
6565
66- let _ = HANDLE . set ( Mutex :: new ( handle) ) ;
66+ if let Ok ( mut guard) = HANDLE . lock ( ) {
67+ * guard = Some ( handle) ;
68+ }
6769}
6870
6971#[ no_mangle]
7072pub extern "C" fn hang_stop_from_c ( ) {
7173 RUNNING . store ( false , Ordering :: Relaxed ) ;
74+ if let Ok ( mut guard) = HANDLE . lock ( ) {
75+ if let Some ( handle) = guard. take ( ) {
76+ let _ = handle. join ( ) ;
77+ }
78+ }
79+ if let Ok ( mut guard) = IMPORT . lock ( ) {
80+ * guard = None ;
81+ }
7282}
7383
7484/// # Safety
@@ -83,14 +93,34 @@ pub unsafe extern "C" fn hang_write_video_packet_from_c(data: *const u8, size: u
8393 return ;
8494 }
8595
86- if let Some ( import_mutex ) = IMPORT . get ( ) {
87- if let Ok ( mut import) = import_mutex . lock ( ) {
96+ if let Ok ( mut guard ) = IMPORT . lock ( ) {
97+ if let Some ( import) = guard . as_mut ( ) {
8898 // SAFETY: Caller of hang_write_video_packet_from_c guarantees data is valid
8999 import. write_video_frame ( data, size, keyframe > 0 , dts) ;
90100 }
91101 }
92102}
93103
104+ /// # Safety
105+ ///
106+ /// The caller must ensure that:
107+ /// - `data` points to a valid buffer of at least `size` bytes
108+ /// - The buffer remains valid for the duration of this function call
109+ #[ no_mangle]
110+ pub unsafe extern "C" fn hang_write_audio_packet_from_c ( data : * const u8 , size : usize , dts : u64 ) {
111+ // Validate pointer and size
112+ if data. is_null ( ) || size == 0 {
113+ return ;
114+ }
115+
116+ if let Ok ( mut guard) = IMPORT . lock ( ) {
117+ if let Some ( import) = guard. as_mut ( ) {
118+ // SAFETY: Caller of hang_write_audio_packet_from_c guarantees data is valid
119+ import. write_audio_frame ( data, size, dts) ;
120+ }
121+ }
122+ }
123+
94124pub async fn client ( url : Url , name : String ) -> anyhow:: Result < ( ) > {
95125 let broadcast = moq_lite:: Broadcast :: produce ( ) ;
96126 let config = moq_native:: ClientConfig :: default ( ) ;
@@ -104,7 +134,9 @@ pub async fn client(url: Url, name: String) -> anyhow::Result<()> {
104134
105135 let mut import = ImportJoy :: new ( broadcast. producer ) ;
106136 import. init ( ) ;
107- let _ = IMPORT . set ( Mutex :: new ( import) ) ;
137+ if let Ok ( mut guard) = IMPORT . lock ( ) {
138+ * guard = Some ( import) ;
139+ }
108140
109141 origin. producer . publish_broadcast ( & name, broadcast. consumer ) ;
110142
@@ -147,11 +179,12 @@ impl ImportJoy {
147179 pub fn init ( & mut self ) {
148180 // Produce the catalog
149181 let mut video_renditions = HashMap :: new ( ) ;
182+ let mut audio_renditions = HashMap :: new ( ) ;
150183
151184 let ( track_name, config) = Self :: init_video ( ) ;
152185 let track = Track {
153186 name : track_name. clone ( ) ,
154- priority : 2 ,
187+ priority : 1 ,
155188 } ;
156189 let track_produce = track. produce ( ) ;
157190 self . broadcast . insert_track ( track_produce. consumer ) ;
@@ -162,14 +195,33 @@ impl ImportJoy {
162195 if !video_renditions. is_empty ( ) {
163196 let video = Video {
164197 renditions : video_renditions,
165- priority : 2 ,
198+ priority : 1 ,
166199 display : None ,
167200 rotation : None ,
168201 flip : None ,
169202 } ;
170203 self . catalog . set_video ( Some ( video) ) ;
171204 }
172205
206+ let ( track_name, config) = Self :: init_audio ( ) ;
207+ let track = Track {
208+ name : track_name. clone ( ) ,
209+ priority : 2 ,
210+ } ;
211+ let track_produce = track. produce ( ) ;
212+ self . broadcast . insert_track ( track_produce. consumer ) ;
213+ audio_renditions. insert ( track_name, config) ;
214+
215+ self . tracks . insert ( 1 , track_produce. producer . into ( ) ) ;
216+
217+ if !audio_renditions. is_empty ( ) {
218+ let audio = Audio {
219+ renditions : audio_renditions,
220+ priority : 2 ,
221+ } ;
222+ self . catalog . set_audio ( Some ( audio) ) ;
223+ }
224+
173225 self . catalog . publish ( ) ;
174226 }
175227
@@ -197,6 +249,20 @@ impl ImportJoy {
197249 ( name, config)
198250 }
199251
252+ pub fn init_audio ( ) -> ( String , AudioConfig ) {
253+ let name = String :: from ( "audio1" ) ;
254+
255+ let config = AudioConfig {
256+ codec : AAC { profile : 2 } . into ( ) ,
257+ sample_rate : 48000 ,
258+ channel_count : 2 ,
259+ bitrate : Some ( 128000 ) ,
260+ description : None ,
261+ } ;
262+
263+ ( name, config)
264+ }
265+
200266 /// # Safety
201267 ///
202268 /// The caller must ensure that `data` points to a valid buffer of at least `size` bytes
@@ -225,4 +291,26 @@ impl ImportJoy {
225291
226292 track. write ( frame) ;
227293 }
294+
295+ /// # Safety
296+ ///
297+ /// The caller must ensure that `data` points to a valid buffer of at least `size` bytes
298+ pub unsafe fn write_audio_frame ( & mut self , data : * const u8 , size : usize , dts : u64 ) {
299+ let Some ( track) = self . tracks . get_mut ( & 1 ) else {
300+ return ;
301+ } ;
302+
303+ // Use copy_from_slice to own the data, avoiding use-after-free when C caller frees the buffer
304+ let payload = Bytes :: copy_from_slice ( std:: slice:: from_raw_parts ( data, size) ) ;
305+
306+ let timestamp = Timestamp :: from_micros ( dts) ;
307+
308+ let frame = Frame {
309+ timestamp,
310+ keyframe : false ,
311+ payload,
312+ } ;
313+
314+ track. write ( frame) ;
315+ }
228316}
0 commit comments