Gstreamer.cpp 68 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568
  1. /*\
  2. |*| Copyright 2015-2016 bill-auger <https://github.com/bill-auger/av-caster/issues>
  3. |*|
  4. |*| This file is part of the AvCaster program.
  5. |*|
  6. |*| AvCaster is free software: you can redistribute it and/or modify
  7. |*| it under the terms of the GNU General Public License version 3
  8. |*| as published by the Free Software Foundation.
  9. |*|
  10. |*| AvCaster is distributed in the hope that it will be useful,
  11. |*| but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. |*| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. |*| GNU General Public License for more details.
  14. |*|
  15. |*| You should have received a copy of the GNU General Public License
  16. |*| along with AvCaster. If not, see <http://www.gnu.org/licenses/>.
  17. \*/
  18. #include <gst/video/videooverlay.h>
  19. #include <gst/video/gstvideosink.h>
  20. #include "AvCaster.h"
  21. #include "Gstreamer.h"
  22. #include "../Trace/TraceGstreamer.h"
  23. /* Gstreamer private class variables */
  24. GstElement* Gstreamer::Pipeline = nullptr ; // Initialize()
  25. GstElement* Gstreamer::ScreencapBin = nullptr ; // Initialize()
  26. GstElement* Gstreamer::ScreenRealSource = nullptr ; // BuildScreencapBin()
  27. GstElement* Gstreamer::ScreenFauxSource = nullptr ; // BuildScreencapBin()
  28. GstElement* Gstreamer::ScreenCapsfilter = nullptr ; // BuildScreencapBin()
  29. GstElement* Gstreamer::CameraBin = nullptr ; // Initialize()
  30. GstElement* Gstreamer::CameraRealSource = nullptr ; // BuildCameraBin()
  31. GstElement* Gstreamer::CameraFauxSource = nullptr ; // BuildCameraBin()
  32. GstElement* Gstreamer::CameraCapsfilter = nullptr ; // BuildCameraBin()
  33. GstElement* Gstreamer::TextBin = nullptr ; // Initialize()
  34. GstElement* Gstreamer::ImageBin = nullptr ; // Initialize()
  35. GstElement* Gstreamer::CompositorBin = nullptr ; // Initialize()
  36. GstPad* Gstreamer::CompositorScreenSinkpad = nullptr ; // BuildCompositorBin()
  37. GstPad* Gstreamer::CompositorCameraSinkpad = nullptr ; // BuildCompositorBin()
  38. GstPad* Gstreamer::CompositorTextSinkpad = nullptr ; // BuildCompositorBin()
  39. GstPad* Gstreamer::CompositorImageSinkpad = nullptr ; // BuildCompositorBin()
  40. #ifndef GST_COMPOSITOR_BUG
  41. GstElement* Gstreamer::CompositorCapsfilter = nullptr ; // BuildCompositorBin()
  42. #endif // GST_COMPOSITOR_BUG
  43. GstElement* Gstreamer::PreviewBin = nullptr ; // Initialize()
  44. GstElement* Gstreamer::PreviewQueue = nullptr ; // BuildPreviewBin()
  45. GstElement* Gstreamer::PreviewFauxSink = nullptr ; // BuildPreviewBin()
  46. GstElement* Gstreamer::PreviewRealSink = nullptr ; // BuildPreviewBin()
  47. GstElement* Gstreamer::AudioBin = nullptr ; // Initialize()
  48. GstElement* Gstreamer::AudioAlsaSource = nullptr ; // BuildAudioBin()
  49. GstElement* Gstreamer::AudioPulseSource = nullptr ; // BuildAudioBin()
  50. GstElement* Gstreamer::AudioJackSource = nullptr ; // BuildAudioBin()
  51. GstElement* Gstreamer::AudioFauxSource = nullptr ; // BuildAudioBin()
  52. GstElement* Gstreamer::AudioCaps = nullptr ; // BuildAudioBin()
  53. GstElement* Gstreamer::MuxerBin = nullptr ; // Initialize()
  54. GstElement* Gstreamer::OutputBin = nullptr ; // Initialize()
  55. GstElement* Gstreamer::OutputQueue = nullptr ; // BuildOutputBin()
  56. GstElement* Gstreamer::OutputFileSink = nullptr ; // BuildOutputBin()
  57. GstElement* Gstreamer::OutputRtmpSink = nullptr ; // BuildOutputBin()
  58. GstElement* Gstreamer::OutputFauxSink = nullptr ; // BuildOutputBin()
  59. ValueTree Gstreamer::ConfigStore = ValueTree::invalid ; // Initialize()
  60. guintptr Gstreamer::PreviewXwin = 0 ; // Initialize()
  61. /* Gstreamer private class methods */
  62. bool Gstreamer::Initialize(ValueTree config_store , void* x_window ,
  63. NamedValueSet& disabled_features )
  64. {
  65. ConfigStore = config_store ;
  66. PreviewXwin = (guintptr)x_window ;
  67. DEBUG_TRACE_GST_INIT_PHASE_1
  68. // initialize gStreamer (NOTE: this will terminate the application on failure)
  69. InitializeGst(nullptr , nullptr) ;
  70. DEBUG_TRACE_GST_INIT_PHASE_2
  71. // determine static pipeline macro configuration
  72. // TODO: ideally it should be robust to always build all bins
  73. // with Is*Enabled() vars merely guarding config/re-config
  74. // but perhaps it is more efficient to simply not build unused bins
  75. // (ASSERT: the 'link bins' section below and Shutdown() reflect the current implementation)
  76. bool is_media_enabled = !disabled_features.contains(CONFIG::OUTPUT_ID ) ;
  77. bool is_screen_enabled = !disabled_features.contains(CONFIG::SCREEN_ID ) ;
  78. bool is_camera_enabled = !disabled_features.contains(CONFIG::CAMERA_ID ) ;
  79. bool is_text_enabled = !disabled_features.contains(CONFIG::TEXT_ID ) ;
  80. bool is_image_enabled = !disabled_features.contains(CONFIG::IMAGE_ID ) ;
  81. bool is_preview_enabled = !disabled_features.contains(CONFIG::PREVIEW_ID) ;
  82. bool is_audio_enabled = !disabled_features.contains(CONFIG::AUDIO_ID ) ;
  83. bool is_vmixer_enabled ;
  84. // assert dependent compositor elements (TODO: remove these restrictions allowing any configuration)
  85. int n_video_inputs = ((is_screen_enabled) ? 1 : 0) + ((is_camera_enabled) ? 1 : 0) +
  86. ((is_text_enabled ) ? 1 : 0) + ((is_image_enabled ) ? 1 : 0) ;
  87. is_vmixer_enabled = is_media_enabled && n_video_inputs == GST::N_COMPOSITOR_INPUTS ;
  88. is_preview_enabled = is_preview_enabled && is_vmixer_enabled ;
  89. bool is_config_sane = !is_media_enabled || is_vmixer_enabled || n_video_inputs == 1 ;
  90. DEBUG_DUMP_MEDIA_SWITCHES
  91. if (!is_config_sane) return false ;
  92. // instantiate pipeline
  93. if ((is_media_enabled && !(Pipeline = NewPipeline(GST::PIPELINE_ID ))) ||
  94. (is_screen_enabled && !(ScreencapBin = NewBin (GST::SCREENCAP_BIN_ID ))) ||
  95. (is_camera_enabled && !(CameraBin = NewBin (GST::CAMERA_BIN_ID ))) ||
  96. (is_text_enabled && !(TextBin = NewBin (GST::TEXT_BIN_ID ))) ||
  97. (is_image_enabled && !(ImageBin = NewBin (GST::IMAGE_BIN_ID ))) ||
  98. (is_vmixer_enabled && !(CompositorBin = NewBin (GST::COMPOSITOR_BIN_ID))) ||
  99. (is_preview_enabled && !(PreviewBin = NewBin (GST::PREVIEW_BIN_ID ))) ||
  100. (is_audio_enabled && !(AudioBin = NewBin (GST::AUDIO_BIN_ID ))) ||
  101. (is_media_enabled && !(MuxerBin = NewBin (GST::MUXER_BIN_ID ))) ||
  102. (is_media_enabled && !(OutputBin = NewBin (GST::OUTPUT_BIN_ID ))) )
  103. { AvCaster::Error(GUI::GST_PIPELINE_INST_ERROR_MSG) ; return false ; }
  104. SetMessageHandler(GST_PIPELINE(Pipeline) , (GstBusSyncHandler)HandleMessage) ;
  105. DEBUG_TRACE_GST_INIT_PHASE_3
  106. // configure pipeline
  107. if ((is_screen_enabled && !AddBin(ScreencapBin )) ||
  108. (is_camera_enabled && !AddBin(CameraBin )) ||
  109. (is_text_enabled && !AddBin(TextBin )) ||
  110. (is_image_enabled && !AddBin(ImageBin )) ||
  111. (is_vmixer_enabled && !AddBin(CompositorBin)) ||
  112. (is_preview_enabled && !AddBin(PreviewBin )) ||
  113. (is_audio_enabled && !AddBin(AudioBin )) ||
  114. (is_media_enabled && !AddBin(MuxerBin )) ||
  115. (is_media_enabled && !AddBin(OutputBin )) )
  116. { AvCaster::Error(GUI::GST_ADD_ERROR_MSG) ; return false ; }
  117. DEBUG_TRACE_GST_INIT_PHASE_4 DEBUG_TRACE_DISABLED_BINS
  118. // configure bins
  119. if ((is_screen_enabled && !BuildScreencapBin ()) ||
  120. (is_camera_enabled && !BuildCameraBin ()) ||
  121. (is_text_enabled && !BuildTextBin ()) ||
  122. (is_image_enabled && !BuildImageBin ()) ||
  123. (is_vmixer_enabled && !BuildCompositorBin()) ||
  124. (is_preview_enabled && !BuildPreviewBin ()) ||
  125. (is_audio_enabled && !BuildAudioBin ()) ||
  126. (is_media_enabled && !BuildMuxerBin ()) ||
  127. (is_media_enabled && !BuildOutputBin ()) )
  128. { AvCaster::Error(GUI::GST_PIPELINE_INIT_ERROR_MSG) ; return false ; }
  129. DEBUG_TRACE_GST_INIT_PHASE_5
  130. // link bins
  131. if (is_vmixer_enabled)
  132. {
  133. if ((is_screen_enabled && !LinkElements(ScreencapBin , CompositorBin)) ||
  134. (is_camera_enabled && !LinkElements(CameraBin , CompositorBin)) ||
  135. (is_text_enabled && !LinkElements(TextBin , CompositorBin)) ||
  136. (is_image_enabled && !LinkElements(ImageBin , CompositorBin)) ||
  137. (is_vmixer_enabled && !LinkElements(CompositorBin , MuxerBin )) ||
  138. (is_preview_enabled && !LinkElements(CompositorBin , PreviewBin )) )
  139. { AvCaster::Error(GUI::VMIXER_BIN_LINK_ERROR_MSG) ; return false ; }
  140. }
  141. else
  142. {
  143. if ((is_screen_enabled && !LinkElements(ScreencapBin , MuxerBin)) ||
  144. (is_camera_enabled && !LinkElements(CameraBin , MuxerBin)) ||
  145. (is_text_enabled && !LinkElements(TextBin , MuxerBin)) ||
  146. (is_image_enabled && !LinkElements(ImageBin , MuxerBin)) )
  147. { AvCaster::Error(GUI::VMIXER_BIN_LINK_ERROR_MSG) ; return false ; }
  148. }
  149. if ((is_audio_enabled && !LinkElements(AudioBin , MuxerBin )) ||
  150. (is_media_enabled && !LinkElements(MuxerBin , OutputBin)) )
  151. { AvCaster::Error(GUI::MUXER_BIN_LINK_ERROR_MSG) ; return false ; }
  152. DEBUG_TRACE_GST_INIT_PHASE_6
  153. // set rolling
  154. if (!SetState(Pipeline , GST_STATE_PLAYING)) return false ;
  155. DEBUG_TRACE_GST_INIT_PHASE_7
  156. // DEBUG_MAKE_GRAPHVIZ
  157. return true ;
  158. }
  159. void Gstreamer::Shutdown()
  160. {
  161. // DEBUG_MAKE_GRAPHVIZ
  162. // TODO: to shut down correctly (flushing the buffers)
  163. // gst_element_send_event(Pipeline , gst_event_eos()) ;
  164. // then wait for EOS message on bus before setting pipeline state to NULL
  165. // FIXME: setting (ScreencapBin to state null here may cause X to throw error:
  166. // "ERROR: X returned BadShmSeg (invalid shared segment parameter) for operation Unknown"
  167. if (!IsInBin(ScreencapBin , ScreenRealSource)) DestroyElement(ScreenRealSource) ;
  168. if (!IsInBin(ScreencapBin , ScreenFauxSource)) DestroyElement(ScreenFauxSource) ;
  169. if (!IsInBin(CameraBin , CameraRealSource)) DestroyElement(CameraRealSource) ;
  170. if (!IsInBin(CameraBin , CameraFauxSource)) DestroyElement(CameraFauxSource) ;
  171. if (!IsInBin(PreviewBin , PreviewRealSink )) DestroyElement(PreviewRealSink ) ;
  172. if (!IsInBin(PreviewBin , PreviewFauxSink )) DestroyElement(PreviewFauxSink ) ;
  173. if (!IsInBin(AudioBin , AudioAlsaSource )) DestroyElement(AudioAlsaSource ) ;
  174. if (!IsInBin(AudioBin , AudioPulseSource)) DestroyElement(AudioPulseSource) ;
  175. if (!IsInBin(AudioBin , AudioJackSource )) DestroyElement(AudioJackSource ) ;
  176. if (!IsInBin(AudioBin , AudioFauxSource )) DestroyElement(AudioFauxSource ) ;
  177. if (!IsInBin(OutputBin , OutputFileSink )) DestroyElement(OutputFileSink ) ;
  178. if (!IsInBin(OutputBin , OutputRtmpSink )) DestroyElement(OutputRtmpSink ) ;
  179. if (!IsInBin(OutputBin , OutputFauxSink )) DestroyElement(OutputFauxSink ) ;
  180. DestroyElement(Pipeline) ;
  181. }
  182. bool Gstreamer::BuildScreencapBin()
  183. {
  184. DEBUG_TRACE_BUILD_SCREENCAP_BIN
  185. GstElement *initial_source , *converter , *queue ;
  186. // instantiate elements
  187. if (!(ScreenRealSource = NewElement(GST::SCREEN_PLUGIN_ID , "screen-real-source")) ||
  188. !(ScreenFauxSource = NewElement(GST::TESTVIDEO_PLUGIN_ID , "screen-faux-source")) ||
  189. !(ScreenCapsfilter = NewElement("capsfilter" , "screen-capsfilter" )) ||
  190. !(converter = NewElement("videoconvert" , "screen-converter" )) ||
  191. !(queue = NewElement("queue" , "screen-queue" )) )
  192. { AvCaster::Error(GUI::SCREENCAP_INIT_ERROR_MSG) ; return false ; }
  193. // configure elements
  194. initial_source = ConfigureScreenBin() ;
  195. ConfigureQueue(queue , 0 , 0 , 0) ;
  196. // link elements
  197. if (!AddElement (ScreencapBin , initial_source ) ||
  198. !AddElement (ScreencapBin , ScreenCapsfilter) ||
  199. !AddElement (ScreencapBin , converter ) ||
  200. !AddElement (ScreencapBin , queue ) ||
  201. !LinkElements (initial_source , ScreenCapsfilter) ||
  202. !LinkElements (ScreenCapsfilter , converter ) ||
  203. !LinkElements (converter , queue ) ||
  204. !NewGhostSrcPad(ScreencapBin , queue , "screen-source"))
  205. { AvCaster::Error(GUI::SCREENCAP_LINK_ERROR_MSG) ; return false ; }
  206. return true ;
  207. }
  208. bool Gstreamer::BuildCameraBin()
  209. {
  210. DEBUG_TRACE_BUILD_CAMERA_BIN
  211. GstElement *initial_source , *converter , *queue ;
  212. // instantiate elements
  213. if (!(CameraRealSource = NewElement(GST::CAMERA_PLUGIN_ID , "camera-real-source")) ||
  214. !(CameraFauxSource = NewElement(GST::TESTVIDEO_PLUGIN_ID , "camera-faux-source")) ||
  215. !(CameraCapsfilter = NewElement("capsfilter" , "camera-capsfilter" )) ||
  216. !(converter = NewElement("videoconvert" , "camera-converter" )) ||
  217. !(queue = NewElement("queue" , "camera-queue" )) )
  218. { AvCaster::Error(GUI::CAMERA_INIT_ERROR_MSG) ; return false ; }
  219. // configure elements
  220. initial_source = ConfigureCameraBin() ;
  221. // link elements
  222. if (!AddElement (CameraBin , initial_source ) ||
  223. !AddElement (CameraBin , CameraCapsfilter) ||
  224. !AddElement (CameraBin , converter ) ||
  225. !AddElement (CameraBin , queue ) ||
  226. !LinkElements (initial_source , CameraCapsfilter) ||
  227. !LinkElements (CameraCapsfilter , converter ) ||
  228. !LinkElements (converter , queue ) ||
  229. !NewGhostSrcPad(CameraBin , queue , "camera-source") )
  230. { AvCaster::Error(GUI::CAMERA_LINK_ERROR_MSG) ; return false ; }
  231. return true ;
  232. }
  233. bool Gstreamer::BuildTextBin()
  234. {
  235. DEBUG_TRACE_BUILD_TEXT_BIN
  236. #ifdef TEXT_BIN_NYI
  237. return false ;
  238. #endif // TEXT_BIN_NYI
  239. GstElement *filesrc , *subparser , *source , *converter , *queue ;
  240. // bool is_enabled = bool (ConfigStore[CONFIG::TEXT_ID ]) ;
  241. String motd_text = STRING(ConfigStore[CONFIG::MOTD_TEXT_ID ]) ;
  242. int text_style_idx = int (ConfigStore[CONFIG::TEXT_STYLE_ID ]) ;
  243. int text_pos_idx = int (ConfigStore[CONFIG::TEXT_POSITION_ID]) ;
  244. // String display_text = (is_enabled) ? motd_text : String::empty ;
  245. /* TODO: include custom font
  246. #include <fontconfig/fontconfig.h>
  247. std::string yourFontFilePath = "/home/testUser/bla.ttf"
  248. const FcChar8 * file = (const FcChar8 *)yourFontFilePath.c_str();
  249. FcBool fontAddStatus = FcConfigAppFOntAddFile(FcConfigGetCurrent(),file);
  250. */
  251. // instantiate elements
  252. if (!(filesrc = gst_element_factory_make("filesrc" , "text-filesrc" )) ||
  253. !(subparser = gst_element_factory_make("subparse" , "text-subparser")) ||
  254. !(source = gst_element_factory_make("textrender" , "text-input" )) ||
  255. !(converter = gst_element_factory_make("videoconvert" , "text-converter")) ||
  256. !(queue = gst_element_factory_make("queue" , "text-queue" )) )
  257. { AvCaster::Error(GUI::TEXT_INIT_ERROR_MSG) ; return false ; }
  258. DEBUG_TRACE_CONFIGURE_TEXT_BIN
  259. // configure elements
  260. ConfigureTextSource(source , "Purisa Normal 40" ) ;
  261. ConfigureFileSource(filesrc , "/code/av-caster/deleteme.srt") ;
  262. // link elements
  263. if (!AddElement (TextBin , filesrc ) ||
  264. !AddElement (TextBin , subparser) ||
  265. !AddElement (TextBin , source ) ||
  266. !AddElement (TextBin , converter) ||
  267. !AddElement (TextBin , queue ) ||
  268. !LinkElements (filesrc , subparser) ||
  269. !LinkElements (subparser , source ) ||
  270. !LinkElements (source , converter) ||
  271. !LinkElements (converter , queue ) ||
  272. !NewGhostSrcPad(TextBin , queue , "text-source"))
  273. { AvCaster::Error(GUI::TEXT_LINK_ERROR_MSG) ; return false ; }
  274. return true ;
  275. }
  276. bool Gstreamer::BuildImageBin()
  277. {
  278. DEBUG_TRACE_BUILD_IMAGE_BIN
  279. //#define STATIC_IMAGE
  280. # ifdef STATIC_IMAGE
  281. /*
  282. GstElement *source , *decoder , *converter ,
  283. *scaler , *scaler_filter ,
  284. *freezer , *freezer_filter , *queue ;
  285. GstCaps *scaler_caps , *freezer_caps ;
  286. bool is_enabled = bool(ConfigStore[CONFIG::IMAGE_ID ]) ;
  287. int interstitial_w = int (ConfigStore[CONFIG::SCREENCAP_W_ID]) ;
  288. int interstitial_h = int (ConfigStore[CONFIG::SCREENCAP_H_ID]) ;
  289. int framerate_idx = int (ConfigStore[CONFIG::FRAMERATE_ID ]) ;
  290. int framerate = CONFIG::FRAMERATES[framerate_idx].getIntValue() ;
  291. String image_filename = "/home/bill/img/tech-diff.png" ;
  292. String scaler_caps_str = String("video/x-raw, ") +
  293. "width=(int)" + String(interstitial_w) + ", " +
  294. "height=(int)" + String(interstitial_h) + ", " +
  295. "framerate=(fraction)0/1, " +
  296. "format=(string)YUY2," +
  297. "interlace-mode=(string)progressive, " +
  298. "pixel-aspect-ratio=(fraction)1/1" ;
  299. String freezer_caps_str = String("video/x-raw, ") +
  300. "width=(int)" + String(interstitial_w) + ", " +
  301. "height=(int)" + String(interstitial_h) + ", " +
  302. "framerate=(fraction)" + String(framerate ) + "/1, " +
  303. "format=(string)YUY2," +
  304. "interlace-mode=(string)progressive, " +
  305. "pixel-aspect-ratio=(fraction)1/1" ;
  306. // instantiate elements
  307. if (!(source = NewElement("filesrc" , "interstitial-real-source" )) ||
  308. !(decoder = NewElement("pngdec" , "interstitial-decoder" )) ||
  309. !(converter = NewElement("videoconvert" , "interstitial-converter" )) ||
  310. !(scaler = NewElement("videoscale" , "interstitial-scaler" )) ||
  311. !(scaler_filter = NewElement("capsfilter" , "interstitial-scaler-caps" )) ||
  312. !(freezer = NewElement("imagefreeze" , "interstitial-freezer" )) ||
  313. !(freezer_filter = NewElement("capsfilter" , "interstitial-freezer-caps")) ||
  314. !(queue = NewElement("queue" , "interstitial-queue" )) ||
  315. !(scaler_caps = NewCaps (scaler_caps_str) ) ||
  316. !(freezer_caps = NewCaps (freezer_caps_str) ) )
  317. { AvCaster::Error(GUI::IMAGE_INIT_ERROR_MSG) ; return false ; }
  318. DEBUG_TRACE_CONFIGURE_IMAGE_BIN
  319. // configure elements
  320. ConfigureFile (source , image_filename ) ;
  321. ConfigureCaps (scaler_filter , scaler_caps ) ;
  322. ConfigureCaps (freezer_filter , freezer_caps ) ;
  323. ConfigureQueue(queue , 0 , 0 , 0) ;
  324. // link elements
  325. if (!AddElement (ImageBin , source ) ||
  326. !AddElement (ImageBin , decoder ) ||
  327. !AddElement (ImageBin , converter ) ||
  328. !AddElement (ImageBin , scaler ) ||
  329. !AddElement (ImageBin , scaler_filter ) ||
  330. !AddElement (ImageBin , freezer ) ||
  331. !AddElement (ImageBin , freezer_filter) ||
  332. !AddElement (ImageBin , queue ) ||
  333. !LinkElements (source , decoder ) ||
  334. !LinkElements (decoder , converter ) ||
  335. !LinkElements (converter , scaler ) ||
  336. !LinkElements (scaler , scaler_filter ) ||
  337. !LinkElements (scaler_filter , freezer ) ||
  338. !LinkElements (freezer , freezer_filter) ||
  339. !LinkElements (freezer_filter , queue ) ||
  340. !NewGhostSrcPad(ImageBin , queue , "interstitial-source"))
  341. { AvCaster::Error(GUI::IMAGE_LINK_ERROR_MSG) ; return false ; }
  342. */
  343. # else // STATIC_IMAGE
  344. GstElement *source , *capsfilter , *converter , *queue ;
  345. // TODO: static image src
  346. int interstitial_w = int(ConfigStore[CONFIG::SCREENCAP_W_ID]) ;
  347. int interstitial_h = int(ConfigStore[CONFIG::SCREENCAP_H_ID]) ;
  348. int framerate_idx = int(ConfigStore[CONFIG::FRAMERATE_ID ]) ;
  349. int framerate = CONFIG::FrameRates()[framerate_idx].getIntValue() ;
  350. String plugin_id = GST::TESTVIDEO_PLUGIN_ID ;
  351. String faux_caps_str = MakeVideoCapsString (interstitial_w , interstitial_h , framerate) ;
  352. //DEBUG_TRACE_CONFIGURE_IMAGE
  353. // instantiate elements
  354. if (!(source = NewElement(plugin_id , "interstitial-real-source")) ||
  355. !(capsfilter = NewElement("capsfilter" , "interstitial-capsfilter" )) ||
  356. !(converter = NewElement("videoconvert" , "interstitial-converter" )) ||
  357. !(queue = NewElement("queue" , "interstitial-queue" )) )
  358. { AvCaster::Error(GUI::IMAGE_INIT_ERROR_MSG) ; return false ; }
  359. // configure elements
  360. ConfigureTestVideo(source , 18 ) ;
  361. ConfigureCaps(capsfilter , faux_caps_str) ;
  362. // link elements
  363. if (!AddElement (ImageBin , source ) ||
  364. !AddElement (ImageBin , capsfilter) ||
  365. !AddElement (ImageBin , converter ) ||
  366. !AddElement (ImageBin , queue ) ||
  367. !LinkElements (source , capsfilter) ||
  368. !LinkElements (capsfilter , converter ) ||
  369. !LinkElements (converter , queue ) ||
  370. !NewGhostSrcPad(ImageBin , queue , "image-source"))
  371. { AvCaster::Error(GUI::IMAGE_LINK_ERROR_MSG) ; return false ; }
  372. # endif // STATIC_IMAGE
  373. return true ;
  374. }
  375. bool Gstreamer::BuildCompositorBin()
  376. {
  377. DEBUG_TRACE_BUILD_COMPOSITOR_BIN
  378. GstElement *screen_queue , *camera_queue , *image_queue ,
  379. #ifndef GST_COMPOSITOR_BUG
  380. *compositor , *converter ,
  381. #else // GST_COMPOSITOR_BUG
  382. *compositor ,
  383. #endif // GST_COMPOSITOR_BUG
  384. *composite_tee , *composite_sink_queue , *composite_thru_queue ;
  385. // instantiate elements
  386. if (!(screen_queue = NewElement("queue" , "compositor-screen-queue")) ||
  387. !(camera_queue = NewElement("queue" , "compositor-camera-queue")) ||
  388. !(image_queue = NewElement("queue" , "compositor-image-queue" )) ||
  389. !(compositor = NewElement("compositor" , "compositor" )) ||
  390. #ifndef GST_COMPOSITOR_BUG
  391. !(CompositorCapsfilter = NewElement("capsfilter" , "compositor-capsfilter" )) ||
  392. !(converter = NewElement("videoconvert" , "compositor-converter" )) ||
  393. #endif // GST_COMPOSITOR_BUG
  394. !(composite_tee = NewElement("tee" , "compositor-tee" )) ||
  395. !(composite_sink_queue = NewElement("queue" , "compositor-sink-queue" )) ||
  396. !(composite_thru_queue = NewElement("queue" , "compositor-thru-queue" )) )
  397. { AvCaster::Error(GUI::VMIXER_INIT_ERROR_MSG) ; return false ; }
  398. // configure elements
  399. ConfigureQueue (screen_queue , 0 , 0 , 0) ;
  400. ConfigureQueue (camera_queue , 0 , 0 , 0) ;
  401. ConfigureQueue (image_queue , 0 , 0 , 0) ;
  402. ConfigureCompositor(compositor , 3 ) ;
  403. ConfigureQueue (composite_sink_queue , 0 , 0 , 0) ;
  404. ConfigureQueue (composite_thru_queue , 0 , 0 , 0) ;
  405. // link elements
  406. if (!AddElement (CompositorBin , screen_queue ) ||
  407. !AddElement (CompositorBin , camera_queue ) ||
  408. !AddElement (CompositorBin , image_queue ) ||
  409. !AddElement (CompositorBin , compositor ) ||
  410. #ifndef GST_COMPOSITOR_BUG
  411. !AddElement (CompositorBin , CompositorCapsfilter) ||
  412. !AddElement (CompositorBin , converter ) ||
  413. #endif // GST_COMPOSITOR_BUG
  414. !AddElement (CompositorBin , composite_tee ) ||
  415. !AddElement (CompositorBin , composite_sink_queue) ||
  416. !AddElement (CompositorBin , composite_thru_queue) ||
  417. #ifndef GST_COMPOSITOR_BUG
  418. !LinkElements(compositor , CompositorCapsfilter) ||
  419. !LinkElements(CompositorCapsfilter , converter ) ||
  420. !LinkElements(converter , composite_tee) )
  421. #else // GST_COMPOSITOR_BUG
  422. !LinkElements(compositor , composite_tee) )
  423. #endif // GST_COMPOSITOR_BUG
  424. { AvCaster::Error(GUI::VMIXER_LINK_ERROR_MSG) ; return false ; }
  425. // instantiate request pads
  426. GstPad *composite_tee_thru_srcpad , *composite_tee_monitor_srcpad ;
  427. if (!NewGhostSinkPad(CompositorBin , screen_queue , "compositor-screen-sink") ||
  428. !NewGhostSinkPad(CompositorBin , camera_queue , "compositor-camera-sink") ||
  429. !NewGhostSinkPad(CompositorBin , image_queue , "compositor-image-sink" ) ||
  430. !(CompositorScreenSinkpad = NewRequestSinkPad(compositor ) ) ||
  431. !(CompositorCameraSinkpad = NewRequestSinkPad(compositor ) ) ||
  432. !(CompositorImageSinkpad = NewRequestSinkPad(compositor ) ) ||
  433. !(composite_tee_thru_srcpad = NewRequestSrcPad (composite_tee) ) ||
  434. !(composite_tee_monitor_srcpad = NewRequestSrcPad (composite_tee) ) )
  435. { AvCaster::Error(GUI::VMIXER_PAD_INIT_ERROR_MSG) ; return false ; }
  436. // configure sink pads
  437. ConfigureCompositorBin() ;
  438. // link ghost pads and request pads
  439. GstPad *screen_thru_srcpad , *camera_thru_srcpad , *image_thru_srcpad ,
  440. *composite_thru_sinkpad , *composite_sink_sinkpad ;
  441. if (!(screen_thru_srcpad = NewStaticSrcPad (screen_queue ) ) ||
  442. !(camera_thru_srcpad = NewStaticSrcPad (camera_queue ) ) ||
  443. !(image_thru_srcpad = NewStaticSrcPad (image_queue ) ) ||
  444. !(composite_thru_sinkpad = NewStaticSinkPad(composite_thru_queue) ) ||
  445. !(composite_sink_sinkpad = NewStaticSinkPad(composite_sink_queue) ) ||
  446. !LinkPads (screen_thru_srcpad , CompositorScreenSinkpad) ||
  447. !LinkPads (camera_thru_srcpad , CompositorCameraSinkpad) ||
  448. !LinkPads (image_thru_srcpad , CompositorImageSinkpad ) ||
  449. !LinkPads (composite_tee_thru_srcpad , composite_thru_sinkpad ) ||
  450. !LinkPads (composite_tee_monitor_srcpad , composite_sink_sinkpad ) ||
  451. !NewGhostSrcPad(CompositorBin , composite_thru_queue , "compositor-source") ||
  452. !NewGhostSrcPad(CompositorBin , composite_sink_queue , "preview-source" ) )
  453. { AvCaster::Error(GUI::VMIXER_PAD_LINK_ERROR_MSG) ; return false ; }
  454. return true ;
  455. }
  456. bool Gstreamer::BuildPreviewBin()
  457. {
  458. DEBUG_TRACE_BUILD_PREVIEW_BIN
  459. GstElement* initial_sink ;
  460. // instantiate elements
  461. if (!(PreviewQueue = NewElement("queue" , "preview-queue" )) ||
  462. !(PreviewRealSink = NewElement(GST::PREVIEW_PLUGIN_ID , GST::PREVIEW_SINK_ID )) ||
  463. !(PreviewFauxSink = NewElement(GST::FAUXSINK_PLUGIN_ID , GST::PREVIEW_FAUXSINK_ID)) )
  464. { AvCaster::Error(GUI::PREVIEW_INIT_ERROR_MSG) ; return false ; }
  465. // configure elements
  466. ConfigureQueue(PreviewQueue , 0 , 0 , 0) ;
  467. initial_sink = ConfigurePreviewBin() ;
  468. // link elements
  469. if (!AddElement (PreviewBin , PreviewQueue ) ||
  470. !AddElement (PreviewBin , initial_sink ) ||
  471. !NewGhostSinkPad(PreviewBin , PreviewQueue , GST::PREVIEW_SINKPAD_ID) ||
  472. !LinkElements (PreviewQueue , initial_sink) )
  473. { AvCaster::Error(GUI::PREVIEW_LINK_ERROR_MSG) ; return false ; }
  474. return true ;
  475. }
  476. bool Gstreamer::BuildAudioBin()
  477. {
  478. DEBUG_TRACE_BUILD_AUDIO_BIN
  479. GstElement *initial_source , *converter , *queue ;
  480. // instantiate elements
  481. if (!(AudioAlsaSource = NewElement(GST::ALSA_PLUGIN_ID , "audio-alsa-source" )) ||
  482. !(AudioPulseSource = NewElement(GST::PULSE_PLUGIN_ID , "audio-pulse-source")) ||
  483. !(AudioJackSource = NewElement(GST::JACK_PLUGIN_ID , "audio-jack-source" )) ||
  484. !(AudioFauxSource = NewElement(GST::TESTAUDIO_PLUGIN_ID , "audio-test-source" )) ||
  485. !(AudioCaps = NewElement("capsfilter" , "audio-capsfilter" )) ||
  486. !(converter = NewElement("audioconvert" , "audio-converter" )) ||
  487. !(queue = NewElement("queue" , "audio-queue" )) )
  488. { AvCaster::Error(GUI::AUDIO_INIT_ERROR_MSG) ; return false ; }
  489. // configure elements
  490. initial_source = ConfigureAudioBin() ;
  491. ConfigureTestAudio(AudioFauxSource) ;
  492. ConfigureQueue(queue , 0 , 0 , 0) ;
  493. // link elements
  494. if (!AddElement (AudioBin , initial_source) ||
  495. !AddElement (AudioBin , AudioCaps ) ||
  496. !AddElement (AudioBin , converter ) ||
  497. !AddElement (AudioBin , queue ) ||
  498. !LinkElements (initial_source , AudioCaps) ||
  499. !LinkElements (AudioCaps , converter) ||
  500. !LinkElements (converter , queue ) ||
  501. !NewGhostSrcPad(AudioBin , queue , "audio-source"))
  502. { AvCaster::Error(GUI::AUDIO_LINK_ERROR_MSG) ; return false ; }
  503. return true ;
  504. }
  505. bool Gstreamer::BuildMuxerBin()
  506. {
  507. DEBUG_TRACE_BUILD_MUXER_BIN
  508. GstElement *video_in_queue , *video_converter , *video_encoder , *video_parser ,
  509. *video_enc_caps , *video_enc_queue ;
  510. GstElement *audio_in_queue , *audio_converter , *audio_encoder , *audio_parser ,
  511. *audio_enc_caps , *audio_enc_queue ;
  512. GstElement *muxer ;
  513. int output_w = int(ConfigStore[CONFIG::OUTPUT_W_ID ]) ;
  514. int output_h = int(ConfigStore[CONFIG::OUTPUT_H_ID ]) ;
  515. int video_bitrate_idx = int(ConfigStore[CONFIG::VIDEO_BITRATE_ID]) ;
  516. int audio_bitrate_idx = int(ConfigStore[CONFIG::AUDIO_BITRATE_ID]) ;
  517. int framerate_idx = int(ConfigStore[CONFIG::FRAMERATE_ID ]) ;
  518. int n_channels = int(ConfigStore[CONFIG::N_CHANNELS_ID ]) ;
  519. int samplerate_idx = int(ConfigStore[CONFIG::SAMPLERATE_ID ]) ;
  520. guint video_bitrate = CONFIG::VideoBitRates() [video_bitrate_idx].getIntValue() ;
  521. guint audio_bitrate = CONFIG::AudioBitRates() [audio_bitrate_idx].getIntValue() ;
  522. int framerate = CONFIG::FrameRates() [framerate_idx ].getIntValue() ;
  523. int samplerate = CONFIG::AudioSampleRates()[samplerate_idx ].getIntValue() ;
  524. String h264_caps_str = MakeH264CapsString(output_w , output_h , framerate) ;
  525. String mp3_caps_str = MakeMp3CapsString(samplerate , n_channels) ;
  526. String video_caps_str = h264_caps_str ;
  527. String audio_caps_str = mp3_caps_str ;
  528. if (!(video_in_queue = NewElement("queue" , "mux-video-queue" )) ||
  529. !(video_converter = NewElement("videoconvert" , "mux-video-converter")) ||
  530. !(video_encoder = NewElement("x264enc" , "mux-video-encoder" )) ||
  531. !(video_parser = NewElement("h264parse" , "mux-video-parser" )) ||
  532. !(video_enc_caps = NewElement("capsfilter" , "mux-video-enc-caps" )) ||
  533. !(video_enc_queue = NewElement("queue" , "mux-video-enc-queue")) ||
  534. !(audio_in_queue = NewElement("queue" , "mux-audio-queue" )) ||
  535. !(audio_converter = NewElement("audioconvert" , "audio-converter" )) ||
  536. !(audio_encoder = NewElement("lamemp3enc" , "mux-audio-encoder" )) ||
  537. !(audio_parser = NewElement("mpegaudioparse" , "mux-audio-parser" )) ||
  538. !(audio_enc_caps = NewElement("capsfilter" , "mux-audio-enc-caps" )) ||
  539. !(audio_enc_queue = NewElement("queue" , "mux-audio-enc-queue")) ||
  540. !(muxer = NewElement("flvmux" , "mux-flvmux" )) )
  541. { AvCaster::Error(GUI::MUXER_INIT_ERROR_MSG) ; return false ; }
  542. DEBUG_TRACE_CONFIGURE_MUXER_BIN
  543. ConfigureX264Encoder(video_encoder , video_bitrate ) ;
  544. ConfigureCaps (video_enc_caps , video_caps_str) ;
  545. ConfigureLameEncoder(audio_encoder , audio_bitrate ) ;
  546. ConfigureCaps (audio_enc_caps , audio_caps_str) ;
  547. ConfigureQueue (audio_enc_queue , 0 , 0 , 0 ) ;
  548. ConfigureFlvmux (muxer) ;
  549. if (!AddElement (MuxerBin , video_in_queue ) ||
  550. !AddElement (MuxerBin , video_converter) ||
  551. !AddElement (MuxerBin , video_encoder ) ||
  552. !AddElement (MuxerBin , video_enc_caps ) ||
  553. !AddElement (MuxerBin , video_parser ) ||
  554. !AddElement (MuxerBin , video_enc_queue) ||
  555. !AddElement (MuxerBin , audio_in_queue ) ||
  556. !AddElement (MuxerBin , audio_converter) ||
  557. !AddElement (MuxerBin , audio_encoder ) ||
  558. !AddElement (MuxerBin , audio_parser ) ||
  559. !AddElement (MuxerBin , audio_enc_caps ) ||
  560. !AddElement (MuxerBin , audio_enc_queue) ||
  561. !AddElement (MuxerBin , muxer ) ||
  562. !LinkElements (video_in_queue , video_converter) ||
  563. # ifdef FAKE_MUX_ENCODER_SRC_AND_SINK
  564. !LinkElements (video_converter , fake_enc_sink ) ||
  565. !LinkElements (fake_enc_src , video_encoder ) ||
  566. # else // FAKE_MUX_ENCODER_SRC_AND_SINK
  567. !LinkElements (video_converter , video_encoder ) ||
  568. # endif // FAKE_MUX_ENCODER_SRC_AND_SINK
  569. !LinkElements (video_encoder , video_enc_caps ) ||
  570. !LinkElements (video_enc_caps , video_parser ) ||
  571. !LinkElements (video_parser , video_enc_queue) ||
  572. !LinkElements (video_enc_queue , muxer ) ||
  573. !NewGhostSinkPad(MuxerBin , video_in_queue , "mux-video-sink") ||
  574. !LinkElements (audio_in_queue , audio_converter) ||
  575. !LinkElements (audio_converter , audio_encoder ) ||
  576. !LinkElements (audio_encoder , audio_enc_caps ) ||
  577. !LinkElements (audio_enc_caps , audio_parser ) ||
  578. !LinkElements (audio_parser , audio_enc_queue) ||
  579. !LinkElements (audio_enc_queue , muxer ) ||
  580. !NewGhostSinkPad(MuxerBin , audio_in_queue , "mux-audio-sink") ||
  581. !NewGhostSrcPad (MuxerBin , muxer , "mux-source" ) )
  582. { AvCaster::Error(GUI::MUXER_LINK_ERROR_MSG) ; return false ; }
  583. return true ;
  584. }
  585. bool Gstreamer::BuildOutputBin()
  586. {
  587. DEBUG_TRACE_BUILD_OUTPUT_BIN
  588. GstElement* initial_sink ;
  589. if (!(OutputQueue = NewElement("queue" , "output-queue" )) ||
  590. !(OutputFileSink = NewElement(GST::FILESINK_PLUGIN_ID , "output-file-sink")) ||
  591. !(OutputRtmpSink = NewElement(GST::RTMPSINK_PLUGIN_ID , "output-rtmp-sink")) ||
  592. !(OutputFauxSink = NewElement(GST::FAUXSINK_PLUGIN_ID , "output-faux-sink")) )
  593. { AvCaster::Error(GUI::OUTPUT_INIT_ERROR_MSG) ; return false ; }
  594. ConfigureQueue(OutputQueue , 0 , 0 , 0) ;
  595. initial_sink = ConfigureOutputBin() ;
  596. if (!AddElement (OutputBin , OutputQueue ) ||
  597. !AddElement (OutputBin , initial_sink) ||
  598. !LinkElements (OutputQueue , initial_sink) ||
  599. !NewGhostSinkPad(OutputBin , OutputQueue , "output-sink"))
  600. { AvCaster::Error(GUI::OUTPUT_LINK_ERROR_MSG) ; return false ; }
  601. return true ;
  602. }
  603. bool Gstreamer::Reconfigure(const Identifier& config_key)
  604. {
  605. bool is_config_pending = AvCaster::GetIsConfigPending() ;
  606. bool configure_all = (config_key == CONFIG::PRESET_ID ||
  607. config_key == CONFIG::IS_PENDING_ID) && !is_config_pending ;
  608. bool configure_screen = config_key == CONFIG::SCREEN_ID || configure_all ;
  609. bool configure_camera = config_key == CONFIG::CAMERA_ID || configure_all ;
  610. bool configure_text = config_key == CONFIG::TEXT_ID || configure_all ;
  611. bool configure_image = config_key == CONFIG::IMAGE_ID || configure_all ;
  612. bool configure_compositor = configure_screen || configure_camera ||
  613. configure_text || configure_image || configure_all ;
  614. bool configure_preview = config_key == CONFIG::PREVIEW_ID ||
  615. config_key == CONFIG::IS_PENDING_ID || configure_all ;
  616. bool configure_audio = config_key == CONFIG::AUDIO_ID || configure_all ;
  617. bool configure_output = config_key == CONFIG::OUTPUT_ID || configure_all ;
  618. DEBUG_TRACE_RECONFIGURE_IN
  619. SetState(Pipeline , GST_STATE_READY) ;
  620. bool is_error = (configure_screen && ConfigureScreenBin() == nullptr) ||
  621. (configure_camera && ConfigureCameraBin() == nullptr) ||
  622. (configure_text && ConfigureTextBin() == nullptr) ||
  623. (configure_image && ConfigureImageBin() == nullptr) ||
  624. (configure_compositor && ConfigureCompositorBin() == false ) ||
  625. (configure_preview && ConfigurePreviewBin() == nullptr) ||
  626. (configure_audio && ConfigureAudioBin() == nullptr) ||
  627. (configure_output && ConfigureOutputBin() == nullptr) ;
  628. DEBUG_TRACE_RECONFIGURE_OUT
  629. // NOTE: an error here should cause Gstreamer::HandleErrorMessage() to fire
  630. // which should attempt recovery to a same configuration
  631. is_error = !SetState(Pipeline , GST_STATE_PLAYING) || is_error ;
  632. // DEBUG_MAKE_GRAPHVIZ
  633. return !is_error ;
  634. }
  635. GstElement* Gstreamer::ConfigureScreenBin()
  636. {
  637. bool is_active = bool(ConfigStore[CONFIG::SCREEN_ID ]) ;
  638. int screencap_w = int (ConfigStore[CONFIG::SCREENCAP_W_ID]) ;
  639. int screencap_h = int (ConfigStore[CONFIG::SCREENCAP_H_ID]) ;
  640. int framerate_idx = int (ConfigStore[CONFIG::FRAMERATE_ID ]) ;
  641. int framerate = CONFIG::FrameRates()[framerate_idx].getIntValue() ;
  642. String screen_caps_str = MakeScreenCapsString(screencap_w , screencap_h , framerate) ;
  643. String faux_caps_str = MakeVideoCapsString (screencap_w , screencap_h , framerate) ;
  644. String caps_str = (is_active) ? screen_caps_str : faux_caps_str ;
  645. GstElement* next_source = (is_active) ? ScreenRealSource : ScreenFauxSource ;
  646. GstElement* current_source = (IsInBin(ScreencapBin , ScreenRealSource)) ? ScreenRealSource :
  647. (IsInBin(ScreencapBin , ScreenFauxSource)) ? ScreenFauxSource :
  648. nullptr ;
  649. DEBUG_TRACE_CONFIGURE_SCREENCAP_BIN
  650. // configure elements
  651. ConfigureScreenSource(ScreenRealSource , screencap_w , screencap_h) ;
  652. ConfigureTestVideo (ScreenFauxSource , 1 ) ;
  653. ConfigureCaps (ScreenCapsfilter , caps_str ) ;
  654. // swap source elements
  655. if (IsInBin(ScreencapBin , current_source) && next_source != current_source)
  656. {
  657. if (!RemoveElement(ScreencapBin , current_source ) ||
  658. !AddElement (ScreencapBin , next_source ) ||
  659. !LinkElements (next_source , ScreenCapsfilter) )
  660. { AvCaster::Error(GUI::SCREENCAP_LINK_ERROR_MSG) ; return nullptr ; }
  661. }
  662. return next_source ;
  663. }
  664. GstElement* Gstreamer::ConfigureCameraBin()
  665. {
  666. // TODO: query device for resolutions (eliminate CONFIG::CAMERA_RESOLUTIONS)
  667. String device_path = AvCaster::GetCameraPath() ;
  668. int framerate = AvCaster::GetCameraRate() ;
  669. Point<int> resolution = AvCaster::GetCameraResolution() ;
  670. int camera_w = resolution.getX() ;
  671. int camera_h = resolution.getY() ;
  672. bool is_active = bool(ConfigStore[CONFIG::CAMERA_ID]) ;
  673. bool use_real_src = is_active && device_path.isNotEmpty() ;
  674. String camera_caps_str = MakeCameraCapsString(camera_w , camera_h , framerate) ;
  675. String faux_caps_str = MakeVideoCapsString (camera_w , camera_h , framerate) ;
  676. String caps_str = (use_real_src) ? camera_caps_str : faux_caps_str ;
  677. GstElement* next_source = (use_real_src) ? CameraRealSource : CameraFauxSource ;
  678. GstElement* current_source = (IsInBin(CameraBin , CameraRealSource)) ? CameraRealSource :
  679. (IsInBin(CameraBin , CameraFauxSource)) ? CameraFauxSource :
  680. nullptr ;
  681. DEBUG_TRACE_CONFIGURE_CAMERA_BIN
  682. // configure elements
  683. ConfigureCameraSource(CameraRealSource , device_path) ;
  684. ConfigureTestVideo (CameraFauxSource , 0 ) ;
  685. ConfigureCaps (CameraCapsfilter , caps_str ) ;
  686. // swap source elements
  687. if (IsInBin(CameraBin , current_source) && next_source != current_source)
  688. {
  689. if (!RemoveElement(CameraBin , current_source ) ||
  690. !AddElement (CameraBin , next_source ) ||
  691. !LinkElements (next_source , CameraCapsfilter) )
  692. { AvCaster::Error(GUI::CAMERA_LINK_ERROR_MSG) ; return nullptr ; }
  693. }
  694. return next_source ;
  695. }
  696. GstElement* Gstreamer::ConfigureTextBin() { return Pipeline ; } // FIXME: should return aTextSource
  697. GstElement* Gstreamer::ConfigureImageBin() { return Pipeline ; } // FIXME: should return anImageSource
  698. bool Gstreamer::ConfigureCompositorBin()
  699. {
  700. bool is_screen_active = bool(ConfigStore[CONFIG::SCREEN_ID ]) ;
  701. bool is_camera_active = bool(ConfigStore[CONFIG::CAMERA_ID ]) ;
  702. #ifndef TEXT_BIN_NYI
  703. bool is_text_active = bool(ConfigStore[CONFIG::TEXT_ID ]) ;
  704. #endif // TEXT_BIN_NYI
  705. bool is_image_active = bool(ConfigStore[CONFIG::IMAGE_ID ]) ;
  706. int screen_w = int (ConfigStore[CONFIG::SCREENCAP_W_ID]) ;
  707. int screen_h = int (ConfigStore[CONFIG::SCREENCAP_H_ID]) ;
  708. int output_w = int (ConfigStore[CONFIG::OUTPUT_W_ID ]) ;
  709. int output_h = int (ConfigStore[CONFIG::OUTPUT_H_ID ]) ;
  710. int framerate_idx = int (ConfigStore[CONFIG::FRAMERATE_ID ]) ;
  711. int framerate = CONFIG::FrameRates()[framerate_idx].getIntValue() ;
  712. Point<int> resolution = AvCaster::GetCameraResolution() ;
  713. int screen_x = 0 ;
  714. int screen_y = 0 ;
  715. int image_w = output_w ;
  716. int image_h = output_h ;
  717. int image_x = 0 ;
  718. int image_y = 0 ;
  719. int camera_w = resolution.getX() ;
  720. int camera_h = resolution.getY() ;
  721. int camera_x = screen_w - camera_w ;
  722. int camera_y = screen_h - camera_h ;
  723. int screen_z = ( is_screen_active ) ? 2 : 0 ;
  724. int camera_z = ( !is_screen_active || is_camera_active) ? 3 : 0 ;
  725. int image_z = ((!is_screen_active && !is_camera_active) || is_image_active) ? 4 : 1 ;
  726. String caps_str = MakeVideoCapsString(output_w , output_h , framerate) ;
  727. DEBUG_TRACE_CONFIGURE_COMPOSITOR_BIN
  728. #ifdef NO_DYNAMIC_MEDIA_Z_ORDER
  729. image_z = 0 ; screen_z = 1 ; camera_z = 2 ;
  730. #endif // NO_DYNAMIC_MEDIA_Z_ORDER
  731. #ifndef GST_COMPOSITOR_BUG
  732. ConfigureCaps (CompositorCapsfilter , caps_str ) ;
  733. #endif // GST_COMPOSITOR_BUG
  734. ConfigureCompositorSink(CompositorScreenSinkpad , screen_w , screen_h ,
  735. screen_x , screen_y , screen_z) ;
  736. ConfigureCompositorSink(CompositorCameraSinkpad , camera_w , camera_h ,
  737. camera_x , camera_y , camera_z) ;
  738. ConfigureCompositorSink(CompositorImageSinkpad , image_w , image_h ,
  739. image_x , image_y , image_z ) ;
  740. return true ;
  741. }
  742. GstElement* Gstreamer::ConfigurePreviewBin()
  743. {
  744. GstElement *current_sink , *next_sink ;
  745. bool is_active = AvCaster::GetIsPreviewActive() ;
  746. current_sink = (IsInBin(PreviewBin , PreviewRealSink)) ? PreviewRealSink :
  747. (IsInBin(PreviewBin , PreviewFauxSink)) ? PreviewFauxSink : nullptr ;
  748. next_sink = (is_active) ? PreviewRealSink : PreviewFauxSink ;
  749. DEBUG_TRACE_CONFIGURE_PREVIEW_BIN
  750. // configure elements
  751. if (!ConfigureVideoSink(PreviewRealSink) && is_active)
  752. AvCaster::Error(GUI::GST_XWIN_ERROR_MSG) ;
  753. // swap sink elements
  754. if (IsInBin(PreviewBin , current_sink) && next_sink != current_sink)
  755. {
  756. if (!RemoveElement(PreviewBin , current_sink) ||
  757. !AddElement (PreviewBin , next_sink ) ||
  758. !LinkElements (PreviewQueue , next_sink ) )
  759. { AvCaster::Error(GUI::PREVIEW_LINK_ERROR_MSG) ; return nullptr ; }
  760. }
  761. return next_sink ;
  762. }
  763. GstElement* Gstreamer::ConfigureAudioBin()
  764. {
  765. GstElement *current_source , *next_source ;
  766. String caps_str ;
  767. // TODO: JACK init fails if samplerate mismatch (issue #37)
  768. bool is_enabled = bool(ConfigStore[CONFIG::AUDIO_ID ]) ;
  769. int audio_api_idx = int (ConfigStore[CONFIG::AUDIO_API_ID ]) ;
  770. int n_channels = int (ConfigStore[CONFIG::N_CHANNELS_ID]) ;
  771. int samplerate_idx = int (ConfigStore[CONFIG::SAMPLERATE_ID]) ;
  772. int samplerate = CONFIG::AudioSampleRates()[samplerate_idx].getIntValue() ;
  773. String audio16_caps_str = MakeAudioCapsString("S16LE" , samplerate , n_channels) ;
  774. String audio32_caps_str = MakeAudioCapsString("F32LE" , samplerate , n_channels) ;
  775. current_source = (IsInBin(AudioBin , AudioAlsaSource )) ? AudioAlsaSource :
  776. (IsInBin(AudioBin , AudioPulseSource)) ? AudioPulseSource :
  777. (IsInBin(AudioBin , AudioJackSource )) ? AudioJackSource :
  778. (IsInBin(AudioBin , AudioFauxSource )) ? AudioFauxSource : nullptr ;
  779. if (!is_enabled) audio_api_idx = CONFIG::INVALID_IDX ;
  780. switch ((CONFIG::AudioApi)audio_api_idx)
  781. {
  782. case CONFIG::ALSA_AUDIO_IDX: next_source = AudioAlsaSource ; caps_str = audio16_caps_str ; break ;
  783. case CONFIG::PULSE_AUDIO_IDX: next_source = AudioPulseSource ; caps_str = audio16_caps_str ; break ;
  784. case CONFIG::JACK_AUDIO_IDX: next_source = AudioJackSource ; caps_str = audio32_caps_str ; break ;
  785. default: next_source = AudioFauxSource ; caps_str = audio16_caps_str ; break ;
  786. }
  787. DEBUG_TRACE_CONFIGURE_AUDIO_BIN
  788. // configure elements
  789. ConfigureCaps(AudioCaps , caps_str) ;
  790. // swap source elements
  791. if (IsInBin(AudioBin , current_source) && current_source != next_source)
  792. {
  793. if (!RemoveElement(AudioBin , current_source) ||
  794. !AddElement (AudioBin , next_source ) ||
  795. !LinkElements (next_source , AudioCaps ) )
  796. { AvCaster::Error(GUI::AUDIO_LINK_ERROR_MSG) ; return nullptr ; }
  797. }
  798. return next_source ;
  799. }
  800. GstElement* Gstreamer::ConfigureOutputBin()
  801. {
  802. bool is_enabled = bool (ConfigStore[CONFIG::OUTPUT_ID ]) ;
  803. int muxer_idx = int (ConfigStore[CONFIG::OUTPUT_MUXER_ID]) ;
  804. int sink_idx = int (ConfigStore[CONFIG::OUTPUT_SINK_ID ]) ;
  805. String destination = STRING(ConfigStore[CONFIG::OUTPUT_DEST_ID ]) ;
  806. String file_ext = CONFIG::OutputMuxers()[muxer_idx] ;
  807. String file_url = MakeFileName(destination , file_ext) ;
  808. String rtmp_url = MakeRtmpUrl (destination) ;
  809. GstElement* current_sink = (IsInBin(OutputBin , OutputFileSink)) ? OutputFileSink :
  810. (IsInBin(OutputBin , OutputRtmpSink)) ? OutputRtmpSink :
  811. (IsInBin(OutputBin , OutputFauxSink)) ? OutputFauxSink :
  812. nullptr ;
  813. GstElement* next_sink ; String output_url ;
  814. #ifdef DISABLE_OUTPUT
  815. UNUSED(is_enabled) ; is_enabled = (false) ? (bool)0 : false ;
  816. #endif // DISABLE_OUTPUT
  817. if (!is_enabled) sink_idx = -1 ;
  818. switch ((CONFIG::OutputStream)sink_idx)
  819. {
  820. case CONFIG::FILE_OUTPUT_IDX: next_sink = OutputFileSink ; output_url = file_url ; break ;
  821. case CONFIG::RTMP_OUTPUT_IDX: next_sink = OutputRtmpSink ; output_url = rtmp_url ; break ;
  822. default: next_sink = OutputFauxSink ; is_enabled = false ; break ;
  823. }
  824. DEBUG_TRACE_CONFIGURE_OUTPUT_BIN
  825. // configure elements
  826. if (is_enabled) ConfigureFileSink(next_sink , output_url) ;
  827. // swap sink elements
  828. if (IsInBin(OutputBin , current_sink) && next_sink != current_sink)
  829. {
  830. if (!RemoveElement(OutputBin , current_sink) ||
  831. !AddElement (OutputBin , next_sink ) ||
  832. !LinkElements (OutputQueue , next_sink ) )
  833. { AvCaster::Error(GUI::OUTPUT_LINK_ERROR_MSG) ; return nullptr ; }
  834. }
  835. return next_sink ;
  836. }
  837. void Gstreamer::ConfigureCaps(GstElement* a_capsfilter , String caps_str)
  838. {
  839. DEBUG_TRACE_CONFIGURE_CAPS
  840. g_object_set(G_OBJECT(a_capsfilter) , "caps" , NewCaps(caps_str) , NULL) ;
  841. }
  842. void Gstreamer::ConfigureQueue(GstElement* a_queue , guint max_bytes ,
  843. guint64 max_time , guint max_buffers)
  844. {
  845. DEBUG_TRACE_CONFIGURE_QUEUE
  846. g_object_set(G_OBJECT(a_queue) , "max-size-bytes" , max_bytes , NULL) ;
  847. g_object_set(G_OBJECT(a_queue) , "max-size-time" , max_time , NULL) ;
  848. g_object_set(G_OBJECT(a_queue) , "max-size-buffers" , max_buffers , NULL) ;
  849. }
  850. void Gstreamer::ConfigureScreenSource(GstElement* a_screen_source , guint capture_w , guint capture_h)
  851. {
  852. DEBUG_TRACE_CONFIGURE_SCREEN
  853. g_object_set(G_OBJECT(a_screen_source) , "endx" , capture_w - 1 , NULL) ;
  854. g_object_set(G_OBJECT(a_screen_source) , "endy" , capture_h - 1 , NULL) ;
  855. g_object_set(G_OBJECT(a_screen_source) , "use-damage" , false , NULL) ;
  856. }
  857. void Gstreamer::ConfigureCameraSource(GstElement* a_camera_source , String device_path)
  858. {
  859. DEBUG_TRACE_CONFIGURE_CAMERA
  860. g_object_set(G_OBJECT(a_camera_source) , "device" , CHARSTAR(device_path) , NULL) ;
  861. }
  862. void Gstreamer::ConfigureTestVideo(GstElement* a_test_source , guint pattern_n)
  863. {
  864. DEBUG_TRACE_CONFIGURE_TEST_VIDEO
  865. g_object_set(G_OBJECT(a_test_source) , "is_live" , (gboolean)true , NULL) ;
  866. g_object_set(G_OBJECT(a_test_source) , "pattern" , (guint )pattern_n , NULL) ;
  867. }
  868. void Gstreamer::ConfigureTextSource(GstElement* a_text_source , String font_desc)
  869. {
  870. DEBUG_TRACE_CONFIGURE_TEXT
  871. g_object_set(G_OBJECT(a_text_source) , "font-desc" , CHARSTAR(font_desc) , NULL) ;
  872. }
  873. void Gstreamer::ConfigureFileSource(GstElement* a_file_source , String location)
  874. {
  875. DEBUG_TRACE_CONFIGURE_FILE_SOURCE
  876. g_object_set(G_OBJECT(a_file_source) , "location" , CHARSTAR(location) , NULL) ;
  877. }
  878. void Gstreamer::ConfigureFileSink(GstElement* a_file_sink , String location)
  879. {
  880. DEBUG_TRACE_CONFIGURE_FILE_SINK
  881. g_object_set(G_OBJECT(a_file_sink) , "location" , CHARSTAR(location) , NULL) ;
  882. }
  883. void Gstreamer::ConfigureCompositor(GstElement* a_compositor , guint background_n)
  884. {
  885. DEBUG_TRACE_CONFIGURE_COMPOSITOR
  886. g_object_set(G_OBJECT(a_compositor) , "background" , background_n , NULL) ;
  887. }
  888. void Gstreamer::ConfigureCompositorSink(GstPad* sinkpad , gint w , gint h ,
  889. gint x , gint y , gint z)
  890. {
  891. DEBUG_TRACE_CONFIGURE_COMPOSITOR_SINK
  892. g_object_set(G_OBJECT(sinkpad) , "width" , w , NULL) ;
  893. g_object_set(G_OBJECT(sinkpad) , "height" , h , NULL) ;
  894. g_object_set(G_OBJECT(sinkpad) , "xpos" , x , NULL) ;
  895. g_object_set(G_OBJECT(sinkpad) , "ypos" , y , NULL) ;
  896. g_object_set(G_OBJECT(sinkpad) , "zorder" , z , NULL) ;
  897. }
  898. bool Gstreamer::ConfigureVideoSink(GstElement* a_video_sink)
  899. {
  900. bool is_active = AvCaster::GetIsPreviewActive() ;
  901. Rectangle<int> preview_bounds = AvCaster::GetPreviewBounds() ;
  902. guintptr x_window_handle = (is_active) ? PreviewXwin : 0 ;
  903. gint preview_x = preview_bounds.getX() ;
  904. gint preview_y = preview_bounds.getY() ;
  905. gint preview_w = preview_bounds.getWidth() ;
  906. gint preview_h = preview_bounds.getHeight() ;
  907. DEBUG_TRACE_CONFIGURE_PREVIEW
  908. gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(a_video_sink) , x_window_handle) ;
  909. // gst_video_overlay_expose(GST_VIDEO_OVERLAY(a_video_sink)) ;
  910. // g_object_set(a_video_sink , "async-handling" , TRUE , NULL) ;
  911. return gst_video_overlay_set_render_rectangle(GST_VIDEO_OVERLAY(a_video_sink) ,
  912. preview_x , preview_y ,
  913. preview_w , preview_h ) ;
  914. }
  915. void Gstreamer::ConfigureTestAudio(GstElement* a_test_source)
  916. {
  917. DEBUG_TRACE_CONFIGURE_TEST_AUDIO
  918. g_object_set(G_OBJECT(a_test_source) , "is_live" , true , NULL) ;
  919. g_object_set(G_OBJECT(a_test_source) , "volume" , 0.1 , NULL) ;
  920. }
  921. void Gstreamer::ConfigureX264Encoder(GstElement* an_x264_encoder , guint bitrate)
  922. {
  923. DEBUG_TRACE_CONFIGURE_X264ENC
  924. g_object_set(G_OBJECT(an_x264_encoder) , "bitrate" , bitrate , NULL) ;
  925. // g_object_set(G_OBJECT(video_encoder) , "tune" , 0x00000004 , NULL) ; // may lower quality in favor of latency
  926. }
  927. void Gstreamer::ConfigureLameEncoder(GstElement* a_lame_encoder , guint bitrate)
  928. {
  929. DEBUG_TRACE_CONFIGURE_LAMEENC
  930. g_object_set(G_OBJECT(a_lame_encoder) , "target" , 1 , NULL) ;
  931. g_object_set(G_OBJECT(a_lame_encoder) , "cbr" , true , NULL) ; // CBR
  932. g_object_set(G_OBJECT(a_lame_encoder) , "bitrate" , bitrate , NULL) ; // CBR
  933. // g_object_set(G_OBJECT(audio_encoder) , "quality" , 2 , NULL) ; // VBR (default is 4) // VBR
  934. }
  935. void Gstreamer::ConfigureFlvmux(GstElement* a_flvmuxer)
  936. {
  937. DEBUG_TRACE_CONFIGURE_FLVMUX
  938. g_object_set(G_OBJECT(a_flvmuxer) , "streamable" , true , NULL) ;
  939. }
  940. bool Gstreamer::SetState(GstElement* an_element , GstState next_state)
  941. {
  942. bool is_err = an_element != nullptr &&
  943. gst_element_set_state(an_element , next_state) == GST_STATE_CHANGE_FAILURE ;
  944. DEBUG_TRACE_SET_GST_STATE
  945. if (is_err) AvCaster::Error(GUI::GST_STATE_ERROR_MSG) ;
  946. return !is_err ;
  947. }
  948. bool Gstreamer::InitializeGst(int *argc , char **argv[]) { gst_init(nullptr , nullptr) ; }
  949. GstBusSyncReply Gstreamer::HandleMessage(GstBus* message_bus , GstMessage* message ,
  950. GstPipeline* pipeline )
  951. {
  952. switch (GST_MESSAGE_TYPE(message))
  953. {
  954. case GST_MESSAGE_ERROR: HandleErrorMessage(message) ; break ;
  955. case GST_MESSAGE_EOS: DEBUG_TRACE_MESSAGE_EOS break ;
  956. case GST_MESSAGE_STATE_CHANGED: DEBUG_TRACE_MESSAGE_STATE_CHANGED break ;
  957. case GST_MESSAGE_STREAM_STATUS: DEBUG_TRACE_DUMP_MESSAGE_STRUCT break ;
  958. default: DEBUG_TRACE_MESSAGE_UNHANDLED break ;
  959. /* GST_MESSAGE_TYPE types
  960. GST_MESSAGE_UNKNOWN = 0,
  961. GST_MESSAGE_EOS = (1 << 0),
  962. GST_MESSAGE_ERROR = (1 << 1),
  963. GST_MESSAGE_WARNING = (1 << 2),
  964. GST_MESSAGE_INFO = (1 << 3),
  965. GST_MESSAGE_TAG = (1 << 4),
  966. GST_MESSAGE_BUFFERING = (1 << 5),
  967. GST_MESSAGE_STATE_CHANGED = (1 << 6),
  968. GST_MESSAGE_STATE_DIRTY = (1 << 7),
  969. GST_MESSAGE_STEP_DONE = (1 << 8),
  970. GST_MESSAGE_CLOCK_PROVIDE = (1 << 9),
  971. GST_MESSAGE_CLOCK_LOST = (1 << 10),
  972. GST_MESSAGE_NEW_CLOCK = (1 << 11),
  973. GST_MESSAGE_STRUCTURE_CHANGE = (1 << 12),
  974. GST_MESSAGE_STREAM_STATUS = (1 << 13),
  975. GST_MESSAGE_APPLICATION = (1 << 14),
  976. GST_MESSAGE_ELEMENT = (1 << 15),
  977. GST_MESSAGE_SEGMENT_START = (1 << 16),
  978. GST_MESSAGE_SEGMENT_DONE = (1 << 17),
  979. GST_MESSAGE_DURATION = (1 << 18),
  980. GST_MESSAGE_LATENCY = (1 << 19),
  981. GST_MESSAGE_ASYNC_START = (1 << 20),
  982. GST_MESSAGE_ASYNC_DONE = (1 << 21),
  983. GST_MESSAGE_ANY = ~0
  984. */
  985. }
  986. /* The result values for a GstBusSyncHandler.
  987. GST_BUS_DROP drop the message
  988. GST_BUS_PASS pass the message to the async queue
  989. GST_BUS_ASYNC pass message to async queue, continue if message is handled
  990. */
  991. return GST_BUS_PASS ;
  992. }
  993. void Gstreamer::HandleErrorMessage(GstMessage* message)
  994. {
  995. GError *error ;
  996. gchar *debug ;
  997. gst_message_parse_error(message , &error , &debug) ;
  998. String error_message = String(error->message) ;
  999. String warning_msg = String::empty ;
  1000. bool is_alsa_init_error = error_message == GST::ALSA_INIT_ERROR ;
  1001. bool is_pulse_init_error = error_message == GST::PULSE_INIT_ERROR ;
  1002. bool is_jack_init_error = error_message == GST::JACK_INIT_ERROR ;
  1003. bool is_xv_init_error = error_message == GST::XV_INIT_ERROR ;
  1004. bool is_file_sink_error = error_message == GST::FILE_SINK_ERROR ;
  1005. DEBUG_TRACE_GST_ERROR_MESSAGE
  1006. // disable control toggle and re-configure with null source or sink
  1007. if (is_alsa_init_error || is_pulse_init_error || is_jack_init_error)
  1008. {
  1009. AvCaster::DeactivateControl(CONFIG::AUDIO_ID) ; ConfigureAudioBin() ;
  1010. warning_msg = (is_alsa_init_error ) ? GUI::ALSA_INIT_ERROR_MSG :
  1011. (is_pulse_init_error) ? GUI::PULSE_INIT_ERROR_MSG :
  1012. (is_jack_init_error ) ? GUI::JACK_INIT_ERROR_MSG : String::empty ;
  1013. }
  1014. else if (is_xv_init_error)
  1015. {
  1016. AvCaster::DeactivateControl(CONFIG::PREVIEW_ID) ; ConfigurePreviewBin() ;
  1017. warning_msg = GUI::XV_INIT_ERROR_MSG ;
  1018. }
  1019. else if (is_file_sink_error)
  1020. {
  1021. AvCaster::DeactivateControl(CONFIG::OUTPUT_ID) ; ConfigureOutputBin() ;
  1022. warning_msg = GUI::FILE_SINK_ERROR_MSG ;
  1023. }
  1024. else { DEBUG_MAKE_GRAPHVIZ }
  1025. // alert user
  1026. if (warning_msg.isNotEmpty()) AvCaster::Warning(warning_msg + error_message) ;
  1027. g_error_free(error) ; g_free(debug) ;
  1028. }
  1029. GstElement* Gstreamer::NewPipeline(String pipeline_id) { return gst_pipeline_new(CHARSTAR(pipeline_id)) ; }
  1030. GstElement* Gstreamer::NewBin(String bin_id) { return gst_bin_new(CHARSTAR(bin_id)) ; }
  1031. GstElement* Gstreamer::NewElement(String plugin_id , String element_id)
  1032. {
  1033. GstElement* new_element = gst_element_factory_make(CHARSTAR(plugin_id) , CHARSTAR(element_id)) ;
  1034. DEBUG_TRACE_MAKE_ELEMENT
  1035. return new_element ;
  1036. }
  1037. GstCaps* Gstreamer::NewCaps(String caps_str)
  1038. {
  1039. GstCaps* new_caps = gst_caps_from_string(CHARSTAR(caps_str)) ;
  1040. DEBUG_TRACE_MAKE_CAPS
  1041. return new_caps ;
  1042. }
  1043. void Gstreamer::SetMessageHandler(GstPipeline* pipeline , GstBusSyncHandler on_message_cb)
  1044. {
  1045. GstBus* message_bus ;
  1046. if (!GST_IS_ELEMENT(pipeline) ||
  1047. !GST_IS_BUS(message_bus = gst_pipeline_get_bus(pipeline)))
  1048. { AvCaster::Error(GUI::GST_BUS_INST_ERROR_MSG) ; return ; }
  1049. gst_bus_set_sync_handler(message_bus , on_message_cb , pipeline , NULL) ;
  1050. gst_object_unref(message_bus) ;
  1051. }
  1052. bool Gstreamer::AddElement(GstElement* a_bin , GstElement* an_element)
  1053. {
  1054. bool is_err = !gst_bin_add(GST_BIN(a_bin) , an_element) ||
  1055. !gst_element_sync_state_with_parent(an_element) ;
  1056. DEBUG_TRACE_ADD_ELEMENT
  1057. return !is_err ;
  1058. }
  1059. bool Gstreamer::RemoveElement(GstElement* a_bin , GstElement* an_element)
  1060. {
  1061. DEBUG_TRACE_REMOVE_ELEMENT_IN
  1062. // bool is_err = !IsInBin(a_bin , an_element) || !SetState(an_element , GST_STATE_NULL) ||
  1063. // !gst_bin_remove(GST_BIN(a_bin) , an_element) ;
  1064. bool is_err = !IsInBin(a_bin , an_element) || !gst_bin_remove(GST_BIN(a_bin) , an_element) ;
  1065. DEBUG_TRACE_REMOVE_ELEMENT_OUT
  1066. return !is_err ;
  1067. }
  1068. void Gstreamer::DestroyElement(GstElement* an_element)
  1069. {
  1070. DEBUG_TRACE_DESTROY_ELEMENT
  1071. // FIXME: on shutdown --> GStreamer-CRITICAL **: gst_object_unref: assertion '((GObject *) object)->ref_count > 0' failed
  1072. gchar* element_name = gst_element_get_name(an_element) ;
  1073. DBG("Gstreamer::DestroyElement(" + String(element_name) + ") refcount=" + String(GST_OBJECT_REFCOUNT_VALUE(an_element))) ;
  1074. g_free(element_name) ;
  1075. if (an_element != nullptr && SetState(an_element , GST_STATE_NULL))
  1076. gst_object_unref(an_element) ;
  1077. }
  1078. bool Gstreamer::AddBin(GstElement* a_bin)
  1079. {
  1080. DEBUG_TRACE_ADD_BIN_IN
  1081. bool is_err = a_bin == nullptr || IsInPipeline(a_bin) ||
  1082. (!gst_bin_add(GST_BIN(Pipeline) , a_bin) ||
  1083. !gst_element_sync_state_with_parent(a_bin) ) ;
  1084. DEBUG_TRACE_ADD_BIN_OUT
  1085. return !is_err ;
  1086. }
  1087. /*
  1088. bool Gstreamer::RemoveBin(GstElement* a_bin)
  1089. {
  1090. DEBUG_TRACE_REMOVE_BIN_IN
  1091. bool is_err = !IsInPipeline(a_bin) || !gst_bin_remove(GST_BIN(Pipeline) , a_bin) ;
  1092. DEBUG_TRACE_REMOVE_BIN_OUT
  1093. return !is_err ;
  1094. }
  1095. */
  1096. bool Gstreamer::LinkElements(GstElement* source , GstElement* sink)
  1097. {
  1098. bool is_err = !gst_element_link(source , sink) ;
  1099. DEBUG_TRACE_LINK_ELEMENTS
  1100. return !is_err ;
  1101. }
  1102. bool Gstreamer::LinkPads(GstPad* srcpad , GstPad* sinkpad)
  1103. {
  1104. bool is_err = gst_pad_link(srcpad , sinkpad) != GST_PAD_LINK_OK ;
  1105. DEBUG_TRACE_LINK_PADS
  1106. return !is_err ;
  1107. }
  1108. GstPad* Gstreamer::NewGhostSrcPad(GstElement* a_bin , GstElement* an_element ,
  1109. String public_pad_id )
  1110. {
  1111. return NewGhostPad(a_bin , an_element , "src" , public_pad_id) ;
  1112. }
  1113. GstPad* Gstreamer::NewGhostSinkPad(GstElement* a_bin , GstElement* an_element ,
  1114. String public_pad_id )
  1115. {
  1116. return NewGhostPad(a_bin , an_element , "sink" , public_pad_id) ;
  1117. }
  1118. GstPad* Gstreamer::NewGhostPad(GstElement* a_bin , GstElement* an_element ,
  1119. String template_id , String public_pad_id)
  1120. {
  1121. const gchar* private_id = UTF8(template_id ) ;
  1122. const gchar* public_id = UTF8(public_pad_id) ;
  1123. GstPad *private_pad , *public_pad ;
  1124. bool is_err = !(private_pad = gst_element_get_static_pad(an_element , private_id )) ||
  1125. !(public_pad = gst_ghost_pad_new (public_id , private_pad)) ||
  1126. !gst_pad_set_active(public_pad , TRUE) ;
  1127. gst_object_unref(private_pad) ;
  1128. DEBUG_TRACE_MAKE_GHOST_PAD
  1129. is_err = is_err || !AddGhostPad(a_bin , public_pad) ;
  1130. if (is_err) gst_object_unref(public_pad) ;
  1131. return (!is_err) ? public_pad : nullptr ;
  1132. }
  1133. bool Gstreamer::AddGhostPad(GstElement* a_bin , GstPad* public_pad)
  1134. {
  1135. bool is_err = a_bin == nullptr || !gst_element_add_pad(a_bin , public_pad) ;
  1136. DEBUG_TRACE_ADD_GHOST_PAD ; UNUSED(is_err) ;
  1137. return !is_err ;
  1138. }
  1139. GstPad* Gstreamer::NewStaticSinkPad(GstElement* an_element)
  1140. {
  1141. return NewStaticPad(an_element , "sink") ;
  1142. }
  1143. GstPad* Gstreamer::NewStaticSrcPad(GstElement* an_element)
  1144. {
  1145. return NewStaticPad(an_element , "src") ;
  1146. }
  1147. GstPad* Gstreamer::NewStaticPad(GstElement* an_element , String template_id)
  1148. {
  1149. const gchar* private_id = UTF8(template_id) ;
  1150. GstPad* private_pad ;
  1151. bool is_err = !(private_pad = gst_element_get_static_pad(an_element , private_id)) ;
  1152. DEBUG_TRACE_GET_STATIC_PAD ; UNUSED(is_err) ;
  1153. return private_pad ;
  1154. }
  1155. GstPad* Gstreamer::NewRequestSinkPad(GstElement* an_element)
  1156. {
  1157. return NewRequestPad(an_element , "sink_%u") ;
  1158. }
  1159. GstPad* Gstreamer::NewRequestSrcPad(GstElement* an_element)
  1160. {
  1161. return NewRequestPad(an_element , "src_%u") ;
  1162. }
  1163. GstPad* Gstreamer::NewRequestPad(GstElement* an_element , String template_id)
  1164. {
  1165. const gchar* private_id = UTF8(template_id) ;
  1166. GstPad* private_pad ;
  1167. bool is_err = !(private_pad = gst_element_get_request_pad(an_element , private_id)) ;
  1168. DEBUG_TRACE_GET_REQUEST_PAD ; UNUSED(is_err) ;
  1169. return private_pad ;
  1170. }
  1171. String Gstreamer::MakeVideoCapsString(int width , int height , int framerate)
  1172. {
  1173. return String("video/x-raw, ") +
  1174. "width=(int)" + String(width ) + ", " +
  1175. "height=(int)" + String(height ) + ", " +
  1176. "framerate=(fraction)" + String(framerate) + "/1, " +
  1177. // "format=ARGB, " +
  1178. "format=I420, " +
  1179. "pixel-aspect-ratio=(fraction)1/1, " +
  1180. "interlace-mode=(string)progressive" ;
  1181. }
  1182. String Gstreamer::MakeScreenCapsString(int screencap_w , int screencap_h , int framerate)
  1183. {
  1184. return String("video/x-raw, ") +
  1185. "width=(int)" + String(screencap_w) + ", " +
  1186. "height=(int)" + String(screencap_h) + ", " +
  1187. "framerate=(fraction)" + String(framerate ) + "/1, " +
  1188. "pixel-aspect-ratio=(fraction)1/1" ;
  1189. }
  1190. String Gstreamer::MakeCameraCapsString(int camera_w , int camera_h , int framerate)
  1191. {
  1192. return String("video/x-raw, ") +
  1193. #ifndef NATIVE_CAMERA_RESOLUTION_ONLY
  1194. "width=(int)" + String(camera_w ) + ", " +
  1195. "height=(int)" + String(camera_h ) + ", " +
  1196. "framerate=(fraction)" + String(framerate) + "/1, " +
  1197. "format=I420, " +
  1198. #endif // NATIVE_CAMERA_RESOLUTION_ONLY
  1199. "pixel-aspect-ratio=(fraction)1/1" ;
  1200. }
  1201. String Gstreamer::MakeAudioCapsString(String format , int samplerate , int n_channels)
  1202. {
  1203. return String("audio/x-raw, " ) +
  1204. String("layout=(string)interleaved, ") +
  1205. String("format=(string)" ) + format + ", " +
  1206. String("rate=(int)" ) + String(samplerate) + ", " +
  1207. String("channels=(int)") + String(n_channels) ;
  1208. //"channel-mask=(bitmask)0x03"
  1209. }
  1210. String Gstreamer::MakeH264CapsString(int output_w , int output_h , int framerate)
  1211. {
  1212. return String("video/x-h264, ") +
  1213. "width=(int)" + String(output_w ) + ", " +
  1214. "height=(int)" + String(output_h ) + ", " +
  1215. "framerate=(fraction)" + String(framerate) + "/1, " +
  1216. "stream-format=avc, alignment=au, profile=main" ;
  1217. // String h264_caps_str = "video/x-h264, level=(string)4.1, profile=main" ;
  1218. }
  1219. String Gstreamer::MakeMp3CapsString(int samplerate , int n_channels)
  1220. {
  1221. return String("audio/mpeg, mpegversion=1, layer=3, ") +
  1222. String("rate=(int)" ) + String(samplerate) + ", " +
  1223. String("channels=(int)") + String(n_channels) ;
  1224. // String mp3_caps_str = String("audio/mpeg, mpegversion=1, layer=3, mpegaudioversion=3, ") +
  1225. }
  1226. String Gstreamer::MakeFileName(String destination , String file_ext)
  1227. {
  1228. String filename = (destination.isEmpty()) ? APP::APP_NAME :
  1229. destination.upToLastOccurrenceOf(file_ext , false , true) ;
  1230. File output_file = APP::videosDir().getNonexistentChildFile(filename , file_ext , false) ;
  1231. return output_file.getFullPathName() ;
  1232. }
  1233. String Gstreamer::MakeRtmpUrl(String destination)
  1234. {
  1235. bool is_lctv = AvCaster::GetPresetIdx() == CONFIG::LCTV_PRESET_IDX ;
  1236. String env_url = std::getenv("AVCASTER_RTMP_DEST") ;
  1237. // String env_url = SystemStats::getEnvironmentVariable("AVCASTER_RTMP_DEST" , "") ;
  1238. destination = destination.retainCharacters(APP::VALID_URI_CHARS) ;
  1239. if (destination.isEmpty()) destination = env_url ;
  1240. if (destination.isEmpty()) AvCaster::Error(GUI::OUTPUT_INIT_ERROR_MSG) ;
  1241. if (is_lctv)
  1242. {
  1243. // trim input allowing either stream key or full url
  1244. if (destination.contains(GST::LCTV_RTMP_URL))
  1245. destination = destination.fromFirstOccurrenceOf(GST::LCTV_RTMP_URL , false , true) ;
  1246. if (destination.contains(" live=1" ))
  1247. destination = destination.upToLastOccurrenceOf (" live=1" , false , true) ;
  1248. destination = GST::LCTV_RTMP_URL + destination + " live=1" ;
  1249. }
  1250. return destination ;
  1251. }
  1252. String Gstreamer::VersionMsg()
  1253. {
  1254. guint major_version , minor_version , micro_version , nano_version ;
  1255. gst_version(&major_version , &minor_version , &micro_version , &nano_version) ;
  1256. return "gStreamer v" + String(major_version) + "." + String(minor_version) +
  1257. "." + String(micro_version) + "." + String(nano_version ) ;
  1258. }
  1259. bool Gstreamer::IsSufficientVersion()
  1260. {
  1261. guint major_version , minor_version , micro_version , nano_version ;
  1262. gst_version(&major_version , &minor_version , &micro_version , &nano_version) ;
  1263. return major_version >= GST::MIN_MAJOR_VERSION &&
  1264. minor_version >= GST::MIN_MINOR_VERSION ;
  1265. }
  1266. String Gstreamer::GetElementId(GstElement* an_element)
  1267. {
  1268. if (!an_element) return "nil" ;
  1269. gchar* id = gst_element_get_name(an_element) ; String element_id = id ; g_free(id) ;
  1270. return element_id ;
  1271. }
  1272. String Gstreamer::GetPadId(GstPad* a_pad)
  1273. {
  1274. if (!a_pad) return "nil" ;
  1275. gchar* id = gst_pad_get_name(a_pad) ; String pad_id = id ; g_free(id) ;
  1276. return pad_id ;
  1277. }
  1278. bool Gstreamer::IsInitialized() { return gst_is_initialized() ; }
  1279. // bool Gstreamer::IsPlaying() { return !!Pipeline && GST_STATE(Pipeline) == GST_STATE_PLAYING ; }
  1280. bool Gstreamer::IsInPipeline(GstElement* an_element)
  1281. {
  1282. return IsInBin(Pipeline , an_element) ;
  1283. }
  1284. bool Gstreamer::IsInBin(GstElement* a_parent_element , GstElement* a_child_element)
  1285. {
  1286. return !!a_child_element && !!a_parent_element &&
  1287. GST_ELEMENT_PARENT(a_child_element) == a_parent_element ;
  1288. }