1717
1818#[ cfg( target_os = "android" ) ]
1919use super :: transmission:: rtp:: RtpSink ;
20- use super :: transmission:: { self , TransmissionSink , hls :: HlsSink } ;
20+ use super :: transmission:: { self , TransmissionSink } ;
2121use anyhow:: Result ;
2222use fcast_lib:: models:: PlayMessage ;
2323#[ cfg( target_os = "android" ) ]
@@ -28,6 +28,7 @@ use log::error;
2828#[ cfg( target_os = "android" ) ]
2929use std:: future:: Future ;
3030use std:: net:: IpAddr ;
31+ use std:: str:: FromStr ;
3132
3233pub use transmission:: init;
3334
@@ -37,13 +38,19 @@ pub enum Event {
3738 Error ,
3839}
3940
41+ pub enum SourceConfig {
42+ AudioVideo {
43+ video : gst:: Element ,
44+ audio : gst:: Element ,
45+ } ,
46+ Video ( gst:: Element ) ,
47+ Audio ( gst:: Element ) ,
48+ }
49+
4050#[ cfg( not( target_os = "android" ) ) ]
4151pub struct Pipeline {
4252 inner : gst:: Pipeline ,
43- tx_sink : Option < Box < dyn TransmissionSink > > ,
44- tee : gst:: Element ,
45- preview_queue : gst:: Element ,
46- preview_appsink : gst:: Element ,
53+ tx_sink : Box < dyn TransmissionSink > ,
4754}
4855
4956#[ cfg( target_os = "android" ) ]
@@ -171,58 +178,69 @@ impl Pipeline {
171178 } )
172179 }
173180
181+ fn setup_video_source ( pipeline : & gst:: Pipeline , src : gst:: Element ) -> Result < gst:: Element > {
182+ // TODO: needed?
183+ let videoflip = gst:: ElementFactory :: make ( "videoflip" )
184+ . property_from_str ( "video-direction" , "auto" )
185+ . build ( ) ?;
186+ let videorate = gst:: ElementFactory :: make ( "videorate" )
187+ . property ( "skip-to-first" , true )
188+ . build ( ) ?;
189+ let capsfilter = gst:: ElementFactory :: make ( "capsfilter" )
190+ . property ( "caps" , gst:: Caps :: from_str ( "video/x-raw,framerate=30/1" ) ?)
191+ . build ( ) ?;
192+
193+ // pipeline.add_many([&src, &videorate, &capsfilter])?;
194+ // gst::Element::link_many([&src, &videorate, &capsfilter])?;
195+
196+ pipeline. add_many ( [ & src, & videoflip, & videorate, & capsfilter] ) ?;
197+ gst:: Element :: link_many ( [ & src, & videoflip, & videorate, & capsfilter] ) ?;
198+
199+ Ok ( capsfilter)
200+ }
201+
202+ fn setup_audio_source ( pipeline : & gst:: Pipeline , src : gst:: Element ) -> Result < gst:: Element > {
203+ let capsfilter = gst:: ElementFactory :: make ( "capsfilter" )
204+ . property (
205+ "caps" ,
206+ gst:: Caps :: from_str ( "audio/x-raw,channels=2,rate=48000" ) ?,
207+ )
208+ . build ( ) ?;
209+
210+ pipeline. add_many ( [ & src, & capsfilter] ) ?;
211+ gst:: Element :: link_many ( [ & src, & capsfilter] ) ?;
212+
213+ Ok ( capsfilter)
214+ }
215+
174216 #[ cfg( not( target_os = "android" ) ) ]
175- pub fn new < E , S > ( preview_appsink : gst :: Element , mut on_event : E , on_sources : S ) -> Result < Self >
217+ pub fn new_rtsp < E > ( mut on_event : E , source : SourceConfig ) -> Result < Self >
176218 where
177219 E : FnMut ( Event ) + Send + Clone + ' static ,
178- S : Fn ( & [ gst:: glib:: Value ] ) -> Option < gst:: glib:: Value > + Send + Sync + ' static ,
179220 {
180- let scapsrc = gst:: ElementFactory :: make ( "scapsrc" )
181- . property ( "perform-internal-preroll" , true )
182- . build ( ) ?;
183- let tee = gst:: ElementFactory :: make ( "tee" ) . build ( ) ?;
184- let preview_queue = gst:: ElementFactory :: make ( "queue" )
185- . name ( "preview_queue" )
186- . property ( "max-size-time" , 0u64 )
187- . property ( "max-size-buffers" , 0u32 )
188- . property ( "max-size-bytes" , 0u32 )
189- . property_from_str ( "leaky" , "downstream" )
190- . property ( "silent" , true ) // Don't emit signals, can give better perf.
191- . build ( ) ?;
221+ use crate :: sender:: transmission:: rtsp:: RtspSink ;
192222
193223 let pipeline = gst:: Pipeline :: new ( ) ;
194224
195- let tx_sink = None :: < Box < dyn TransmissionSink > > ;
196-
197- // https://gitlab.freedesktop.org/gstreamer/gstreamer/-/issues/3993
198- scapsrc. static_pad ( "src" ) . unwrap ( ) . add_probe (
199- gst:: PadProbeType :: QUERY_UPSTREAM . union ( gst:: PadProbeType :: PUSH ) ,
200- |_pad, info| match info. query_mut ( ) . map ( |query| query. view_mut ( ) ) {
201- Some ( gst:: QueryViewMut :: Latency ( latency) ) => {
202- let ( _live, min, max) = latency. result ( ) ;
203- latency. set ( false , min, max) ;
204- gst:: PadProbeReturn :: Handled
205- }
206- _ => gst:: PadProbeReturn :: Pass ,
225+ let source = match source {
226+ SourceConfig :: AudioVideo { video, audio } => SourceConfig :: AudioVideo {
227+ video : Self :: setup_video_source ( & pipeline, video) ?,
228+ audio : Self :: setup_audio_source ( & pipeline, audio) ?,
207229 } ,
208- ) ;
209-
210- scapsrc . connect ( "select-source" , false , on_sources ) ;
211-
212- pipeline . add_many ( [ & scapsrc , & tee , & preview_queue , & preview_appsink ] ) ? ;
213- gst :: Element :: link_many ( [ & scapsrc , & tee ] ) ? ;
214- gst :: Element :: link_many ( [ & preview_queue , & preview_appsink ] ) ? ;
230+ SourceConfig :: Video ( video ) => {
231+ SourceConfig :: Video ( Self :: setup_video_source ( & pipeline , video ) ? )
232+ }
233+ SourceConfig :: Audio ( audio ) => {
234+ SourceConfig :: Audio ( Self :: setup_audio_source ( & pipeline , audio ) ? )
235+ }
236+ } ;
215237
216- let tee_preview_pad = tee
217- . request_pad_simple ( "src_%u" )
218- . map_or_else ( || Err ( anyhow:: anyhow!( "`request_pad_simple()` failed" ) ) , Ok ) ?;
219- let queue_preview_pad = preview_queue
220- . static_pad ( "sink" )
221- . ok_or ( anyhow:: anyhow!( "preview_queue is missing static sink pad" ) ) ?;
222- tee_preview_pad. link ( & queue_preview_pad) ?;
238+ let rtsp = RtspSink :: new ( & pipeline, source, 3000 ) ?;
239+ let p = Self {
240+ inner : pipeline. clone ( ) ,
241+ tx_sink : Box :: new ( rtsp) ,
242+ } ;
223243
224- // Start the pipeline in background thread because `scapsrc` initialization will block until
225- // the user selects the input source.
226244 let _ = std:: thread:: spawn ( {
227245 let bus = pipeline
228246 . bus ( )
@@ -236,9 +254,11 @@ impl Pipeline {
236254 debug ! ( "Failed to upgrade pipeline before starting" ) ;
237255 return ;
238256 } ;
239- debug ! ( "Starting pipeline" ) ;
257+ debug ! ( "Starting pipeline... " ) ;
240258 if let Err ( err) = pipeline. set_state ( gst:: State :: Playing ) {
241259 error ! ( "Failed to start pipeline: {err}" ) ;
260+ } else {
261+ debug ! ( "Pipeline started" ) ;
242262 }
243263 }
244264
@@ -278,20 +298,11 @@ impl Pipeline {
278298 }
279299 } ) ;
280300
281- Ok ( Self {
282- inner : pipeline,
283- tx_sink,
284- tee,
285- preview_queue,
286- preview_appsink,
287- } )
301+ Ok ( p)
288302 }
289303
290304 pub fn playing ( & mut self ) -> Result < ( ) > {
291- match & mut self . tx_sink {
292- Some ( sink) => sink. playing ( ) ,
293- None => Ok ( ( ) ) ,
294- }
305+ self . tx_sink . playing ( )
295306 }
296307
297308 #[ cfg( target_os = "android" ) ]
@@ -308,27 +319,7 @@ impl Pipeline {
308319 #[ cfg( not( target_os = "android" ) ) ]
309320 pub fn shutdown ( & mut self ) -> Result < ( ) > {
310321 self . inner . set_state ( gst:: State :: Null ) ?;
311-
312- self . preview_queue . unlink ( & self . preview_appsink ) ;
313- self . inner . remove ( & self . preview_appsink ) ?;
314-
315- if let Some ( sink) = & mut self . tx_sink {
316- sink. shutdown ( ) ;
317- }
318-
319- Ok ( ( ) )
320- }
321-
322- #[ cfg( not( target_os = "android" ) ) ]
323- pub fn add_hls_sink ( & mut self , port : u16 ) -> Result < ( ) > {
324- let tee_pad = self
325- . tee
326- . request_pad_simple ( "src_%u" )
327- . ok_or ( anyhow:: anyhow!( "`request_pad_simple()` failed" ) ) ?;
328- let hls = HlsSink :: new ( & self . inner , tee_pad, port) ?;
329- self . tx_sink = Some ( Box :: new ( hls) ) ;
330-
331- debug ! ( "Added HLS sink" ) ;
322+ self . tx_sink . shutdown ( ) ;
332323
333324 Ok ( ( ) )
334325 }
@@ -348,20 +339,6 @@ impl Pipeline {
348339 Ok ( ( ) )
349340 }
350341
351- #[ cfg( not( target_os = "android" ) ) ]
352- pub fn add_rtsp_sink ( & mut self , port : u16 ) -> Result < ( ) > {
353- let tee_pad = self
354- . tee
355- . request_pad_simple ( "src_%u" )
356- . ok_or ( anyhow:: anyhow!( "`request_pad_simple()` failed" ) ) ?;
357- let rtsp = transmission:: rtsp:: RtspSink :: new ( tee_pad, & self . inner , port) ?;
358- self . tx_sink = Some ( Box :: new ( rtsp) ) ;
359-
360- debug ! ( "Added RTSP sink" ) ;
361-
362- Ok ( ( ) )
363- }
364-
365342 #[ cfg( target_os = "android" ) ]
366343 pub fn add_rtp_sink ( & mut self , port : u16 , receiver_addr : IpAddr ) -> Result < ( ) > {
367344 let appsrc_pad = self
@@ -376,26 +353,9 @@ impl Pipeline {
376353 Ok ( ( ) )
377354 }
378355
379- pub fn remove_transmission_sink ( & mut self ) -> Result < ( ) > {
380- if let Some ( sink) = & mut self . tx_sink {
381- sink. shutdown ( ) ;
382- sink. unlink ( & self . inner ) ?;
383- }
384-
385- self . tx_sink = None ;
386-
387- debug ! ( "Removed transmission sink" ) ;
388-
389- Ok ( ( ) )
390- }
391-
392356 /// Get the message that should be sent to a receiver to consume the stream if a transmission
393357 /// sink is present
394358 pub fn get_play_msg ( & self , addr : IpAddr ) -> Option < PlayMessage > {
395- if let Some ( sink) = & self . tx_sink {
396- sink. get_play_msg ( addr)
397- } else {
398- None
399- }
359+ self . tx_sink . get_play_msg ( addr)
400360 }
401361}
0 commit comments