Argus Camera Sample
Argus Camera Sample
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
VideoPipeline.cpp
Go to the documentation of this file.
1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2016 - 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3  * SPDX-License-Identifier: LicenseRef-NvidiaProprietary
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * * Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * * Neither the name of NVIDIA CORPORATION nor the names of its
14  * contributors may be used to endorse or promote products derived
15  * from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
25  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #include <stdio.h>
31 
32 #include <string>
33 
34 #include "Error.h"
35 #include "VideoPipeline.h"
36 #include "Composer.h"
37 #include "Util.h"
38 
39 namespace ArgusSamples
40 {
41 
43 #ifdef GST_SUPPORTED
44  : m_state(GST_STATE_NULL)
45  , m_pipeline(NULL)
46 #endif
47 {
48 }
49 
51 {
52  destroy();
53 }
54 
55 ///! give the video eoncoder a name so we can find it at stop()
56 static const char *s_videoEncoderName = "video encoder";
57 
58 /**
59  * RAII helper class for calling gst_object_unref on exit from a block or function.
60  */
61 template <typename T> class GstUnrefer
62 {
63 public:
64  explicit GstUnrefer(T * p)
65  : m_p(p)
66  {
67  }
69  : m_p(NULL)
70  {
71  }
73  {
74  release();
75  }
76 
77  /// Cancel the unref.
78  void cancel()
79  {
80  m_p = NULL;
81  }
82 
83  /// Unref the object now.
84  void release()
85  {
86  if (m_p)
87  gst_object_unref(m_p);
88  m_p = NULL;
89  }
90 
91  /// Set the object to be unrefed.
92  void set(T* p)
93  {
94  release();
95  m_p = p;
96  }
97 
98  /// Get the object.
99  T * get() const
100  {
101  return m_p;
102  }
103 
104 private:
105  T *m_p;
106 
107  /// Not implemented -- use default constructor
108  GstUnrefer(GstUnrefer& other);
109  /// Not implemented
111 };
112 
113 bool VideoPipeline::setupForRecording(EGLStreamKHR videoStream, uint32_t width, uint32_t height,
114  float frameRate, const char *fileName, VideoFormat videoFormat,
115  VideoFileType videoFileType, uint32_t bitRate, VideoControlRateMode controlRate,
116  bool enableTwoPassCBR)
117 {
118 #ifdef GST_SUPPORTED
119  // set the filename
120  std::string videoFileName(fileName);
121  if (videoFileName != "/dev/null")
122  {
123  videoFileName += ".";
124  videoFileName += getFileExtension(videoFileType);
125  PROPAGATE_ERROR(validateOutputPath(videoFileName.c_str()));
126  }
127 
128  // Init gstreamer
129  gst_init(NULL, NULL);
130 
131  // create the pipeline
132  m_pipeline = gst_pipeline_new("video_pipeline");
133  if (!m_pipeline)
134  ORIGINATE_ERROR("Failed to create video pipeline");
135 
136  // Create the capture source element
137  GstElement *videoSource = gst_element_factory_make("nveglstreamsrc", NULL);
138  if (!videoSource)
139  ORIGINATE_ERROR("Failed to create capture source element");
140  GstUnrefer<GstElement> unrefer(videoSource);
141  if (!gst_bin_add(GST_BIN(m_pipeline), videoSource))
142  ORIGINATE_ERROR("Failed to add video source to pipeline");
143  unrefer.cancel();
144 
145  g_object_set(G_OBJECT(videoSource), "display", Composer::getInstance().getEGLDisplay(), NULL);
146  g_object_set(G_OBJECT(videoSource), "eglstream", videoStream, NULL);
147 
148  // Create queue
149  GstElement *queue = gst_element_factory_make("queue", NULL);
150  if (!queue)
151  ORIGINATE_ERROR("Failed to create queue");
152  unrefer.set(queue);
153  if (!gst_bin_add(GST_BIN(m_pipeline), queue))
154  ORIGINATE_ERROR("Failed to add queue to pipeline");
155  unrefer.cancel();
156 
157  // create the encoder
158  GstElement *videoEncoder = NULL;
159  switch (videoFormat)
160  {
161  case VIDEO_FORMAT_H264:
162  videoEncoder = gst_element_factory_make("nvv4l2h264enc", s_videoEncoderName);
163  break;
164  case VIDEO_FORMAT_H265:
165  videoEncoder = gst_element_factory_make("nvv4l2h265enc", s_videoEncoderName);
166  break;
167  case VIDEO_FORMAT_VP8:
168  printf("\n***vp8 encode is not supported for Jetson-Xavier & beyond\n");
169  videoEncoder = gst_element_factory_make("nvv4l2vp8enc", s_videoEncoderName);
170  break;
171  case VIDEO_FORMAT_VP9:
172  videoEncoder = gst_element_factory_make("nvv4l2vp9enc", s_videoEncoderName);
173  break;
174  default:
175  ORIGINATE_ERROR("Unhandled video format");
176  }
177  if (!videoEncoder)
178  ORIGINATE_ERROR("Failed to create video encoder");
179  unrefer.set(videoEncoder);
180  if (!gst_bin_add(GST_BIN(m_pipeline), videoEncoder))
181  ORIGINATE_ERROR("Failed to add video encoder to pipeline");
182  unrefer.cancel();
183 
184  // if no bitrate is given select from reasonable presets
185  if (bitRate == 0)
186  {
187  if (height < 720)
188  bitRate = VIDEO_BITRATE_4M;
189  else if (height < 1080)
190  bitRate = VIDEO_BITRATE_8M;
191  else if (height <= 2160)
192  bitRate = VIDEO_BITRATE_14M;
193  else
194  bitRate = VIDEO_BITRATE_20M;
195 
196  if (frameRate < 15.0)
197  {
198  bitRate = (uint32_t)((float)bitRate/30.0*frameRate);
199  }
200  }
201 
202  g_object_set(G_OBJECT(videoEncoder), "bitrate", bitRate, NULL);
203  g_object_set(G_OBJECT(videoEncoder), "control-rate", controlRate, NULL);
204 
205  // TODO: Bug 5398832: remove when the feature is supported in openrm stack also.
206  bool result = (bool)system("lsmod | grep 'nvgpu' > /dev/null");
207  if (result == false)
208  {
209  //nvgpu module is loaded; use twoPassCBR for video encoding on nvgpu based stack
210  g_object_set(G_OBJECT(videoEncoder), "EnableTwopassCBR", enableTwoPassCBR, NULL);
211  }
212 
213  /*
214  * Currently, of all the supported videoEncoders above: H264, H265, VP8 and VP9, Only H265
215  * supports resolution > 4k.
216  */
217  const uint32_t WIDTH_4K = 3840;
218  if (width > WIDTH_4K && videoFormat != VIDEO_FORMAT_H265)
219  {
220  ORIGINATE_ERROR("\n Resolution > 4k requires videoformat H265 \n");
221  }
222  // set video encoding profile for h.264 to high to get optmized video quality
223  if (videoFormat == VIDEO_FORMAT_H264)
224  {
225  g_object_set(G_OBJECT(videoEncoder), "profile", VIDEO_AVC_PROFILE_HIGH, NULL);
226  }
227 
228  // create the muxer
229  if (videoFormat == VIDEO_FORMAT_VP9)
230  {
231  printf("\nThe VP9 video format is not supported on Jetson-tx1.\n");
232  }
233 
234  if ((videoFileType == VIDEO_FILE_TYPE_3GP) &&
235  !(videoFormat == VIDEO_FORMAT_H264))
236  {
237  printf("\nThe 3GP is only supported with H264 in current GST version. "
238  "Selecting other containers.\n");
239  videoFileType = VIDEO_FILE_TYPE_MKV;
240  }
241 
242  if ((videoFileType == VIDEO_FILE_TYPE_AVI) &&
243  (videoFormat == VIDEO_FORMAT_H265))
244  {
245  printf("\nThe AVI is not supported with H265 in current GST version. "
246  "Selecting other containers.\n");
247  videoFileType = VIDEO_FILE_TYPE_MKV;
248  }
249 
250  GstElement *videoParse = NULL;
251  switch (videoFormat)
252  {
253  case VIDEO_FORMAT_H264:
254  videoParse = gst_element_factory_make("h264parse", NULL);
255  if (!videoParse)
256  ORIGINATE_ERROR("Failed to create video parser");
257  break;
258  case VIDEO_FORMAT_H265:
259  videoParse = gst_element_factory_make("h265parse", NULL);
260  if (!videoParse)
261  ORIGINATE_ERROR("Failed to create video parser");
262  break;
263  case VIDEO_FORMAT_VP9:
264  case VIDEO_FORMAT_VP8:
265  break;
266  default:
267  ORIGINATE_ERROR("Unhandled video file type");
268  }
269  if (videoParse)
270  {
271  unrefer.set(videoParse);
272  if (!gst_bin_add(GST_BIN(m_pipeline), videoParse))
273  ORIGINATE_ERROR("Failed to add video parser to pipeline");
274  unrefer.cancel();
275  }
276 
277  GstElement *videoMuxer = NULL;
278  switch (videoFileType)
279  {
280  case VIDEO_FILE_TYPE_MP4:
281  videoMuxer = gst_element_factory_make("qtmux", NULL);
282  break;
283  case VIDEO_FILE_TYPE_3GP:
284  videoMuxer = gst_element_factory_make("3gppmux", NULL);
285  break;
286  case VIDEO_FILE_TYPE_AVI:
287  videoMuxer = gst_element_factory_make("avimux", NULL);
288  break;
289  case VIDEO_FILE_TYPE_MKV:
290  videoMuxer = gst_element_factory_make("matroskamux", NULL);
291  break;
292  default:
293  ORIGINATE_ERROR("Unhandled video file type");
294  }
295  if (!videoMuxer)
296  ORIGINATE_ERROR("Failed to create video muxer");
297  unrefer.set(videoMuxer);
298  if (!gst_bin_add(GST_BIN(m_pipeline), videoMuxer))
299  ORIGINATE_ERROR("Failed to add video muxer to pipeline");
300  unrefer.cancel();
301 
302  // create the sink
303  GstElement *videoSink = gst_element_factory_make("filesink", NULL);
304  if (!videoSink)
305  ORIGINATE_ERROR("Failed to create video sink");
306  unrefer.set(videoSink);
307  if (!gst_bin_add(GST_BIN(m_pipeline), videoSink))
308  ORIGINATE_ERROR("Failed to add video sink to pipeline");
309  unrefer.cancel();
310 
311  g_object_set(G_OBJECT(videoSink), "location", videoFileName.c_str(), NULL);
312 
313  // @todo 'Floating point exception' and error 'Framerate set to : 0 at
314  // NvxVideoEncoderSetParameter' when no setting the framerate. '0' should be VFR, use 30
315  // instead
316  if (frameRate == 0.0f)
317  frameRate = 30.0f;
318 
319  // create a caps filter
320  GstCaps *caps = gst_caps_new_simple("video/x-raw",
321  "format", G_TYPE_STRING, "NV12",
322  "width", G_TYPE_INT, width,
323  "height", G_TYPE_INT, height,
324  "framerate", GST_TYPE_FRACTION, static_cast<gint>(frameRate * 100.f), 100,
325  NULL);
326  if (!caps)
327  ORIGINATE_ERROR("Failed to create caps");
328 
329  GstCapsFeatures *feature = gst_caps_features_new("memory:NVMM", NULL);
330  if (!feature)
331  {
332  gst_caps_unref(caps);
333  ORIGINATE_ERROR("Failed to create caps feature");
334  }
335 
336  gst_caps_set_features(caps, 0, feature);
337 
338  // link the source to the queue via the capture filter
339  if (!gst_element_link_filtered(videoSource, queue, caps))
340  {
341  gst_caps_unref(caps);
342  ORIGINATE_ERROR("Failed to link source to queue");
343  }
344  gst_caps_unref(caps);
345 
346  // link the queue to the encoder
347  if (!gst_element_link(queue, videoEncoder))
348  ORIGINATE_ERROR("Failed to link queue to encoder");
349 
350  // link the encoder pad to the muxer
351  if (videoParse)
352  {
353  if (!gst_element_link(videoEncoder, videoParse))
354  ORIGINATE_ERROR("Failed to link encoder to parser");
355 
356  if (!gst_element_link(videoParse, videoMuxer))
357  ORIGINATE_ERROR("Failed to link parser to muxer");
358  }
359  else
360  {
361  if (!gst_element_link(videoEncoder, videoMuxer))
362  ORIGINATE_ERROR("Failed to link encoder to muxer");
363  }
364 
365  // link the muxer to the sink
366  if (!gst_element_link(videoMuxer, videoSink))
367  ORIGINATE_ERROR("Failed to link muxer to sink");
368 
369  return true;
370 #else // GST_SUPPORTED
371  ORIGINATE_ERROR("Not supported");
372 #endif // GST_SUPPORTED
373 }
374 
375 #ifdef GST_SUPPORTED
376 /**
377  * Modify object flag values by name.
378  */
379 static bool objectModifyFlags(GObject *obj, const char *flagName, const char *valueName, bool set)
380 {
381  guint count;
382  GParamSpec **spec = g_object_class_list_properties(G_OBJECT_GET_CLASS(obj), &count);
383 
384  for (guint index = 0; index < count; ++index)
385  {
386  GParamSpec *param = spec[index];
387  if (strcmp(param->name, flagName) == 0)
388  {
389  if (!G_IS_PARAM_SPEC_FLAGS(param))
390  ORIGINATE_ERROR("Param '%s' is not a flag", flagName);
391 
392  GParamSpecFlags *pflags = G_PARAM_SPEC_FLAGS(param);
393  GFlagsValue *value = g_flags_get_value_by_nick(pflags->flags_class, valueName);
394  if (!value)
395  ORIGINATE_ERROR("Value '%s' of flag '%s' not found", valueName, flagName);
396 
397  gint flags;
398  g_object_get(obj, flagName, &flags, NULL);
399  if (set)
400  flags |= value->value;
401  else
402  flags &= ~value->value;
403  g_object_set(obj, flagName, flags, NULL);
404 
405  return true;
406  }
407  }
408 
409  ORIGINATE_ERROR("Param '%s' not found", flagName);
410 }
411 #endif // GST_SUPPORTED
412 
413 bool VideoPipeline::setupForPlayback(EGLStreamKHR *videoStream, const char *fileName)
414 {
415 #ifdef GST_SUPPORTED
416  // Init gstreamer
417  gst_init(NULL, NULL);
418 
419  // Create the source element
420  m_pipeline = gst_element_factory_make("playbin", "play");
421  if (!m_pipeline)
422  ORIGINATE_ERROR("Failed to create playback pipeline");
423 
424  // set the uri
425  char *uri = gst_filename_to_uri(fileName, NULL);
426  g_object_set(G_OBJECT(m_pipeline), "uri", uri, NULL);
427  g_free(uri);
428  uri = NULL;
429 
430  PROPAGATE_ERROR(objectModifyFlags(G_OBJECT(m_pipeline), "flags", "text", false));
431  PROPAGATE_ERROR(objectModifyFlags(G_OBJECT(m_pipeline), "flags", "native-video", true));
432 
433  // create the audio sink
434  GstElement *audioSink = gst_element_factory_make("autoaudiosink", "audio_sink");
435  if (!audioSink)
436  ORIGINATE_ERROR("Failed to create audio sink");
437 
438  // set the audio sink of the pipeline
439  g_object_set(G_OBJECT(m_pipeline), "audio-sink", audioSink, NULL);
440 
441  // Create the sink bin, this will hold the video converter and the video sink
442  GstElement *videoSinkBin = gst_bin_new("video_sink_bin");
443  if (!videoSinkBin)
444  ORIGINATE_ERROR("Failed to create video sink bin");
445 
446  // set the video sink of the pipeline
447  g_object_set(G_OBJECT(m_pipeline), "video-sink", videoSinkBin, NULL);
448 
449  // Create the video converted
450  GstElement *videoConvert = gst_element_factory_make("nvvidconv", "video converter");
451  if (!videoConvert)
452  ORIGINATE_ERROR("Failed to create video converter");
453  GstUnrefer<GstElement> unrefer(videoConvert);
454  if (!gst_bin_add(GST_BIN(videoSinkBin), videoConvert))
455  ORIGINATE_ERROR("Failed to add video convert to video sink bin");
456  unrefer.cancel();
457 
458  // Create the video sink
459  GstElement *videoSink = gst_element_factory_make("nvvideosink", "video sink");
460  if (!videoSink)
461  ORIGINATE_ERROR("Failed to create video sink");
462  unrefer.set(videoSink);
463  if (!gst_bin_add(GST_BIN(videoSinkBin), videoSink))
464  ORIGINATE_ERROR("Failed to add video sink to video sink bin");
465  unrefer.cancel();
466 
467  // configure video sink
468  g_object_set(G_OBJECT(videoSink), "display", Composer::getInstance().getEGLDisplay(), NULL);
469  // get the EGL stream
470  *videoStream = EGL_NO_STREAM_KHR;
471  g_object_get(G_OBJECT(videoSink), "stream", videoStream, NULL);
472  if (*videoStream == EGL_NO_STREAM_KHR)
473  ORIGINATE_ERROR("Failed to get EGL stream from video sink");
474 
475  if (!gst_element_link(videoConvert, videoSink))
476  ORIGINATE_ERROR("Failed to link video convert to video sink");
477 
478  // create a ghost pad so that the pipeline can connect to the bin as a sink
479  GstPad *pad = gst_element_get_static_pad(videoConvert, "sink");
480  if (!pad)
481  ORIGINATE_ERROR("Failed to get sink pad of video convert");
482  GstUnrefer<GstPad> padUnrefer(pad);
483  GstPad *ghostPad = gst_ghost_pad_new("sink", pad);
484  if (!ghostPad)
485  ORIGINATE_ERROR("Failed to create the ghost pad");
486  GstUnrefer<GstPad> ghostPadUnrefer(ghostPad);
487  if (!gst_pad_set_active(ghostPad, TRUE))
488  ORIGINATE_ERROR("Failed to set pad active");
489  if (!gst_element_add_pad(videoSinkBin, ghostPad))
490  ORIGINATE_ERROR("Failed to add pad");
491  ghostPadUnrefer.cancel();
492  padUnrefer.release();
493 
494  return true;
495 #else // GST_SUPPORTED
496  ORIGINATE_ERROR("Not supported");
497 #endif // GST_SUPPORTED
498 }
499 
501 {
502 #ifdef GST_SUPPORTED
503  if (!m_pipeline)
504  ORIGINATE_ERROR("Video pipeline is not set up");
505 
506  if (m_state != GST_STATE_PLAYING)
507  {
508  // set to playing state
509  if (gst_element_set_state(m_pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE)
510  ORIGINATE_ERROR("Failed to set playing state");
511 
512  m_state = GST_STATE_PLAYING;
513 
514  /* Dump Capture - Playing Pipeline into the dot file
515  * Set environment variable "export GST_DEBUG_DUMP_DOT_DIR=/tmp"
516  * Run argus_camera and 0.00.00.*-argus_camera.dot
517  * file will be generated.
518  * Run "dot -Tpng 0.00.00.*-argus_camera.dot > image.png"
519  * image.png will display the running capture pipeline.
520  * */
521  GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS(GST_BIN(m_pipeline),
522  GST_DEBUG_GRAPH_SHOW_ALL, "argus_camera");
523  }
524 
525  return true;
526 #else // GST_SUPPORTED
527  ORIGINATE_ERROR("Not supported");
528 #endif // GST_SUPPORTED
529 }
530 
532 {
533 #ifdef GST_SUPPORTED
534  if (!m_pipeline)
535  ORIGINATE_ERROR("Video pipeline is not set up");
536 
537  if (m_state != GST_STATE_PAUSED)
538  {
539  if (gst_element_set_state(m_pipeline, GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE)
540  ORIGINATE_ERROR("Failed to set pause state");
541  m_state = GST_STATE_PAUSED;
542  }
543 
544  return true;
545 #else // GST_SUPPORTED
546  ORIGINATE_ERROR("Not supported");
547 #endif // GST_SUPPORTED
548 }
549 
550 
552 {
553 #ifdef GST_SUPPORTED
554  if (!m_pipeline)
555  ORIGINATE_ERROR("Video pipeline is not set up");
556 
557  GstState newState = GST_STATE_NULL;
558  if (m_state == GST_STATE_PLAYING)
559  newState = GST_STATE_PAUSED;
560  else if (m_state == GST_STATE_PAUSED)
561  newState = GST_STATE_PLAYING;
562  else
563  ORIGINATE_ERROR("Invalid state");
564 
565  if (gst_element_set_state(m_pipeline, newState) == GST_STATE_CHANGE_FAILURE)
566  ORIGINATE_ERROR("Failed to set pause state");
567 
568  m_state = newState;
569 
570  return true;
571 #else // GST_SUPPORTED
572  ORIGINATE_ERROR("Not supported");
573 #endif // GST_SUPPORTED
574 }
575 
577 {
578 #ifdef GST_SUPPORTED
579  if (!m_pipeline)
580  ORIGINATE_ERROR("Video pipeline is not set up");
581 
582  if (!gst_element_seek_simple(m_pipeline, GST_FORMAT_TIME,
583  static_cast<GstSeekFlags>(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT), 0))
584  {
585  ORIGINATE_ERROR("Failed to rewind");
586  }
587 
588  return true;
589 #else // GST_SUPPORTED
590  ORIGINATE_ERROR("Not supported");
591 #endif // GST_SUPPORTED
592 }
593 
595 {
596 #ifdef GST_SUPPORTED
597  if (!m_pipeline)
598  ORIGINATE_ERROR("Video pipeline is not set up");
599 
600  if ((m_state == GST_STATE_PLAYING) || (m_state == GST_STATE_PAUSED))
601  {
602  // check if there is a video encoder
603  GstElement *videoEncoder = gst_bin_get_by_name(GST_BIN(m_pipeline), s_videoEncoderName);
604  if (videoEncoder)
605  {
606  // send the end of stream event
607  GstPad *pad = gst_element_get_static_pad(videoEncoder, "sink");
608  if (!pad)
609  ORIGINATE_ERROR("Failed to get 'sink' pad");
610  GstUnrefer<GstPad> padUnrefer(pad);
611  if (!gst_pad_send_event(pad, gst_event_new_eos()))
612  ORIGINATE_ERROR("Failed to send end of stream event encoder");
613  padUnrefer.release();
614 
615  // wait for the event to go through
616  GstBus *bus = gst_pipeline_get_bus(GST_PIPELINE(m_pipeline));
617  if (!bus)
618  ORIGINATE_ERROR("Failed to get bus");
619  GstUnrefer<GstBus> busUnrefer(bus);
620  if (!gst_bus_poll(bus, GST_MESSAGE_EOS, GST_CLOCK_TIME_NONE))
621  ORIGINATE_ERROR("Failed to wait for the eof event");
622  busUnrefer.release();
623  }
624 
625  // stop the pipeline
626  if (gst_element_set_state(m_pipeline, GST_STATE_NULL) != GST_STATE_CHANGE_SUCCESS)
627  ORIGINATE_ERROR("Failed to stop pipeline");
628 
629  m_state = GST_STATE_NULL;
630  }
631 
632  return true;
633 #else // GST_SUPPORTED
634  ORIGINATE_ERROR("Not supported");
635 #endif // GST_SUPPORTED
636 }
637 
639 {
640 #ifdef GST_SUPPORTED
641  if (m_pipeline)
642  {
643  PROPAGATE_ERROR(stop());
644 
645  // delete pipeline
646  gst_object_unref(GST_OBJECT(m_pipeline));
647 
648  m_pipeline = NULL;
649  }
650 
651  return true;
652 #else // GST_SUPPORTED
653  ORIGINATE_ERROR("Not supported");
654 #endif // GST_SUPPORTED
655 }
656 
657 /*static*/ const char* VideoPipeline::getFileExtension(VideoFileType fileType)
658 {
659  switch (fileType)
660  {
661  case VIDEO_FILE_TYPE_MP4:
662  return "mp4";
663  case VIDEO_FILE_TYPE_3GP:
664  return "3gp";
665  case VIDEO_FILE_TYPE_AVI:
666  return "avi";
667  case VIDEO_FILE_TYPE_MKV:
668  return "mkv";
670  return "h265";
671  default:
672  break;
673  }
674 
675  return "Unhandled video file type";
676 }
677 
678 bool VideoPipeline::getAspectRatio(float *aspectRatio) const
679 {
680  if (aspectRatio == NULL)
681  ORIGINATE_ERROR("'aspectRatio' is NULL");
682 #ifdef GST_SUPPORTED
683  if ((m_state != GST_STATE_PLAYING) && (m_state != GST_STATE_PAUSED))
684  ORIGINATE_ERROR("Must be in paused or playing state.");
685 
686  GstState state = GST_STATE_NULL;
687  while ((state != GST_STATE_PLAYING) && (state != GST_STATE_PAUSED))
688  {
689  if (gst_element_get_state(m_pipeline, &state, NULL, GST_CLOCK_TIME_NONE) ==
690  GST_STATE_CHANGE_FAILURE)
691  {
692  ORIGINATE_ERROR("gst_element_get_state failed");
693  }
694  }
695 
696  // Retrieve the Caps at the entrance of the video sink
697  GstElement *videoSink;
698  g_object_get(m_pipeline, "video-sink", &videoSink, NULL);
699  if (!videoSink)
700  ORIGINATE_ERROR("Failed to get video-sink");
701  GstUnrefer<GstElement> videoSinkUnrefer(videoSink);
702 
703  GstPad *videoSinkPad = gst_element_get_static_pad(videoSink, "sink");
704  if (!videoSinkPad)
705  ORIGINATE_ERROR("Failed to get video-sink pad");
706 
707  GstCaps *caps = gst_pad_get_current_caps(videoSinkPad);
708  if (!caps)
709  ORIGINATE_ERROR("Failed to get video-sink pad caps");
710 
711  *aspectRatio = 1.0f;
712 
713  GstStructure *structure = gst_caps_get_structure(caps, 0);
714  if (!structure)
715  {
716  gst_caps_unref(caps);
717  ORIGINATE_ERROR("Failed to get caps structure");
718  }
719 
720  gint width, height;
721  gint pixelAspectRatioNumerator, pixelAspectRatioDenominator;
722 
723  if (!gst_structure_get_int(structure, "width", &width) ||
724  !gst_structure_get_int(structure, "height", &height) ||
725  !gst_structure_get_fraction(structure, "pixel-aspect-ratio",
726  &pixelAspectRatioNumerator, &pixelAspectRatioDenominator))
727  {
728  gst_caps_unref(caps);
729  ORIGINATE_ERROR("Failed to get structure values");
730  }
731 
732  *aspectRatio = (float)width / (float)height;
733  *aspectRatio *= (float)pixelAspectRatioNumerator / (float)pixelAspectRatioDenominator;
734 
735  gst_caps_unref(caps);
736 
737  return true;
738 #else // GST_SUPPORTED
739  ORIGINATE_ERROR("Not supported");
740 #endif // GST_SUPPORTED
741 }
742 
744 {
745 #ifdef GST_SUPPORTED
746  return true;
747 #else // GST_SUPPORTED
748  return false;
749 #endif // GST_SUPPORTED
750 }
751 
752 }; // namespace ArgusSamples