Guest User

Untitled

a guest
Aug 21st, 2019
113
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. //Copyright (c) 2019 Michael de Gans
  2. //
  3. //Permission is hereby granted, free of charge, to any person obtaining a copy
  4. // of this software and associated documentation files (the "Software"), to deal
  5. //in the Software without restriction, including without limitation the rights
  6. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. //copies of the Software, and to permit persons to whom the Software is
  8. //furnished to do so, subject to the following conditions:
  9. //
  10. //The above copyright notice and this permission notice shall be included in all
  11. // copies or substantial portions of the Software.
  12. //
  13. //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18. //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  19. //SOFTWARE.
  20.  
  21. #include "pipeline.h"
  22.  
  23. // these create the branches of the pipeline
  24. gboolean create_pipeline_begin(PipelineData* p_data);
  25. gboolean create_encoder_branch(PipelineData* p_data, const gchar* filename);
  26. gboolean create_nvinfer_branch(PipelineData* p_data);
  27.  
  28. // this links the entire pipeline together
  29. gboolean link_pipeline(PipelineData* p_data);
  30.  
  31. // main pipeline creation function
  32. gboolean create_pipeline_data(PipelineData* p_data, const gchar* filename) {
  33. // create the branches of the pipeline ...
  34. if (!create_pipeline_begin(p_data))
  35. return cleanup_pipeline_data(p_data);
  36. if (!create_encoder_branch(p_data, filename))
  37. return cleanup_pipeline_data(p_data);
  38. if (!create_nvinfer_branch(p_data))
  39. return cleanup_pipeline_data(p_data);
  40.  
  41. // ... and link them together
  42. if (!link_pipeline(p_data))
  43. return cleanup_pipeline_data(p_data);
  44.  
  45. // dump a pipeline graph to file
  46. GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(p_data->pipeline), GST_DEBUG_GRAPH_SHOW_ALL,
  47. "pipeline");
  48.  
  49. return TRUE;
  50. }
  51.  
  52. GstElement* create_and_add_element(GstPipeline* pipeline, const gchar* name) {
  53. // make an element
  54. GstElement* elem = gst_element_factory_make(name, name);
  55.  
  56. // verify if was created and fail if not
  57. if (!GST_IS_ELEMENT(elem)) {
  58. GST_ERROR(ERR_ELEM, name);
  59. return NULL;
  60. }
  61.  
  62. // add the element to the pipeline bin
  63. gst_bin_add(GST_BIN(pipeline), elem);
  64.  
  65. return elem;
  66. }
  67.  
  68. gboolean create_pipeline_begin(PipelineData* p_data) {
  69. // create a new Pipeline (Bin subclass) and check that it exists
  70. p_data->pipeline = GST_PIPELINE(gst_pipeline_new("pipeline"));
  71. if (!GST_IS_PIPELINE(p_data->pipeline)) {
  72. GST_ERROR(ERR_PIPELINE_CREATION);
  73. return FALSE;
  74. }
  75.  
  76. // a handy pointer to the bus to avoid having to call
  77. // gst_pipeline_get_bus repeatedly.
  78. // this must be unreferenced in the cleanup function.
  79. p_data->bus = gst_pipeline_get_bus(p_data->pipeline);
  80. if (!GST_IS_BUS(p_data->bus)) {
  81. GST_ERROR(ERR_BUS_GET);
  82. return FALSE;
  83. }
  84.  
  85. // create the camera, and add it to the Pipeline
  86. #ifdef IS_TEGRA // use the CSI camera, TODO: add usb camera support
  87. p_data->camera = create_and_add_element(p_data->pipeline, BC_CAMERA_CSI);
  88. if (!GST_IS_ELEMENT(p_data->camera)) {
  89. GST_ERROR(ERR_CAMERA_CREATION);
  90. return FALSE;
  91. }
  92. g_object_set(G_OBJECT(p_data->camera), "aeantibanding",
  93. 0, // disables flicker reduction
  94. "maxperf", TRUE, // enable argus maximum performance
  95. "ee-mode", 2, // edge enhancement off
  96. "tnr-mode", 2, // high quality temporal noise reduction
  97. NULL);
  98. #else
  99. // TODO(x86) make separate source and converter element
  100. GST_ERROR("X86 support unimplimentted");
  101. return FALSE;
  102. #endif
  103. // create a capsfilter element to tell the camera what we want sent downstream
  104. p_data->capsfilter =
  105. create_and_add_element(p_data->pipeline, BC_ELEM_CAPS_FILTER);
  106. if (p_data->capsfilter == NULL)
  107. return FALSE;
  108. g_object_set(G_OBJECT(p_data->capsfilter), "caps",
  109. gst_caps_from_string(BC_CAPS_STRING), NULL);
  110.  
  111. // create a tee (T) junction element to split the pipeline into two branches
  112. // (encoder branch and inference branch)
  113. p_data->tee = create_and_add_element(p_data->pipeline, BC_ELEM_TEE);
  114. if (p_data->tee == NULL)
  115. return FALSE;
  116.  
  117. return TRUE;
  118. }
  119.  
  120. gboolean create_encoder_branch(PipelineData* p_data, const gchar* filename) {
  121. // create the encoder queue to buffer data and run everything downstream
  122. // in it's own thread
  123. p_data->enc_queue = gst_element_factory_make(BC_ELEM_QUEUE, "enc_queue");
  124. if (!p_data->enc_queue) {
  125. GST_ERROR(ERR_ELEM, "(encoder) queue");
  126. return FALSE;
  127. }
  128. gst_bin_add(GST_BIN(p_data->pipeline), p_data->enc_queue);
  129.  
  130. // create and configure the h265 encoder
  131. p_data->encoder = create_and_add_element(p_data->pipeline, BC_ELEM_ENCODER);
  132. if (p_data->encoder == NULL)
  133. return FALSE;
  134. g_object_set(G_OBJECT(p_data->encoder), "bitrate", 4000000, NULL);
  135.  
  136. // create the parser
  137. p_data->parser = create_and_add_element(p_data->pipeline, BC_ELEM_PARSER);
  138. if (p_data->parser == NULL)
  139. return FALSE;
  140.  
  141. // create and configure the muxer
  142. p_data->muxer = create_and_add_element(p_data->pipeline, BC_ELEM_MUXER);
  143. if (p_data->muxer == NULL)
  144. return FALSE;
  145. g_object_set(G_OBJECT(p_data->muxer), "writing-app", "birbcam", NULL);
  146. // write index every minute so if something happens, the file will still be
  147. // seekable (probably, haven't tested this) TODO: test this
  148. g_object_set(G_OBJECT(p_data->muxer), "min-index-interval", (guint64)6e+10,
  149. NULL);
  150.  
  151. // filesink
  152. p_data->filesink = create_and_add_element(p_data->pipeline, BC_ELEM_FILESINK);
  153. if (p_data->filesink == NULL)
  154. return FALSE;
  155. g_object_set(G_OBJECT(p_data->filesink), "location", filename, NULL);
  156.  
  157. return TRUE;
  158. }
  159.  
  160. gboolean create_nvinfer_branch(PipelineData* p_data) {
  161. // create the inference queue to run everything downstream in it's own thread
  162. // and to buffer input
  163. p_data->infer_queue = gst_element_factory_make(BC_ELEM_QUEUE, "infer_queue");
  164. if (!p_data->infer_queue) {
  165. GST_ERROR("Could not create inference queue.");
  166. return FALSE;
  167. }
  168. gst_bin_add(GST_BIN(p_data->pipeline), p_data->infer_queue);
  169.  
  170. // create nvstreammux element to add the metadata
  171. p_data->streammux =
  172. create_and_add_element(p_data->pipeline, BC_ELEM_STREAM_MUX);
  173. if (p_data->streammux == NULL)
  174. return FALSE;
  175. g_object_set(G_OBJECT(p_data->streammux), "batch-size", 1, "width", 384,
  176. "height", 216, NULL);
  177.  
  178. // create primary inference element (no secondary as of yet, maybe use the
  179. // Coral, but will need to write a plugin for that since Google's is written
  180. // in python, no really)
  181. p_data->infer = create_and_add_element(p_data->pipeline, BC_ELEM_INFERENCE);
  182. if (p_data->infer == NULL)
  183. return FALSE;
  184. g_object_set(G_OBJECT(p_data->infer), "config-file-path", "../birbcam.cfg",
  185. NULL);
  186.  
  187. // create a fakesink, onto which a probe will be attached to call on_batch
  188. // for each batch of frames to parse the metadata
  189. p_data->fakesink = create_and_add_element(p_data->pipeline, BC_ELEM_FAKESINK);
  190. if (p_data->fakesink == NULL) {
  191. return FALSE;
  192. }
  193. return TRUE;
  194. }
  195.  
  196. gboolean link_pipeline(PipelineData* p_data) {
  197. #ifdef IS_TEGRA
  198. // link pipeline beginning
  199. if (!gst_element_link_many(p_data->camera, p_data->capsfilter, p_data->tee,
  200. NULL)) {
  201. // "Could not link pipeline %s."
  202. GST_ERROR(ERR_LINK, "beginning");
  203. return FALSE;
  204. }
  205. #else // X86
  206. // link: camera, converter, capsfilter, tee
  207. #endif
  208. // link and connect encoder branch
  209. if (!gst_element_link_many(p_data->enc_queue, p_data->encoder, p_data->parser,
  210. p_data->muxer, p_data->filesink, NULL)) {
  211. GST_ERROR(ERR_LINK, "encoder branch");
  212. return FALSE;
  213. }
  214.  
  215. // // link and connect inference branch
  216. if (!gst_element_link_pads(p_data->infer_queue, "src", p_data->streammux,
  217. "sink_0")) {
  218. GST_ERROR(ERR_LINK, "inference queue and stream muxer");
  219. return FALSE;
  220. }
  221. if (!gst_element_link_many(p_data->streammux, p_data->infer, p_data->fakesink,
  222. NULL)) {
  223. GST_ERROR(ERR_LINK, "inference branch");
  224. return FALSE;
  225. }
  226.  
  227. // link the branches to the tee
  228. if (!gst_element_link(p_data->tee, p_data->enc_queue)) {
  229. GST_ERROR(ERR_LINK, "tee and encoder queue");
  230. }
  231. if (!gst_element_link(p_data->tee, p_data->infer_queue)) {
  232. GST_ERROR(ERR_LINK, "tee and inference queue");
  233. }
  234.  
  235.  
  236. return TRUE;
  237. }
  238.  
  239. gboolean shutdown_pipeline(PipelineData* p_data) {
  240. // set the pipeline to the playing state
  241. gst_element_set_state(GST_ELEMENT(p_data->pipeline), GST_STATE_NULL);
  242. }
  243.  
  244. gboolean cleanup_pipeline_data(PipelineData* p_data) {
  245. // unreference the bus because gst_pipeline_get_bus requires it
  246. if (p_data->bus) {
  247. gst_object_unref(p_data->bus);
  248. }
  249. shutdown_pipeline(p_data);
  250. // unreference the pipeline (and all elements added to it implicitly)
  251. if (p_data->pipeline)
  252. gst_object_unref(p_data->pipeline);
  253. return FALSE;
  254. }
RAW Paste Data