YARP
Yet Another Robot Platform
H264Decoder.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2006-2020 Istituto Italiano di Tecnologia (IIT)
3  * All rights reserved.
4  *
5  * This software may be modified and distributed under the terms of the
6  * BSD-3-Clause license. See the accompanying LICENSE file for details.
7  */
8 
9 #include "H264Decoder.h"
10 #include "H264LogComponent.h"
11 
12 #include <yarp/os/LogStream.h>
13 
14 
15 #include <gst/gst.h>
16 #include <glib.h>
17 
18 #include <gst/app/gstappsink.h>
19 #include <cstdio>
20 #include <cstring>
21 #include <mutex>
22 
23 //#define debug_time 1
24 
25 #ifdef debug_time
26  #include <yarp/os/Time.h>
27  #define DBG_TIME_PERIOD_PRINTS 10 //10 sec
28 #endif
29 
30 using namespace yarp::sig;
31 using namespace yarp::os;
32 
33 
35 {
36  data_for_gst_callback() = default;
37 
38  std::mutex *m{nullptr};
39  ImageOf<PixelRgb> *img{nullptr};
40  bool isNew{false};
41  Semaphore *s{nullptr};
42  bool isReq{false};
43 };
44 //-------------------------------------------------------------------
45 //--------------- CALLBACK FUNCTIONS -------------------------------
46 //-------------------------------------------------------------------
47 
48 /*
49 static GstBusSyncReply bus_call (GstBus *bus, GstMessage *msg, gpointer data)
50 {
51  GstElement *pipeline = (GstElement *) data;
52 
53  switch (GST_MESSAGE_TYPE (msg))
54  {
55 
56  case GST_MESSAGE_EOS:
57  {
58  yCTrace(H264CARRIER, "End of stream");
59  gst_element_set_state (pipeline, GST_STATE_NULL);
60  // g_main_loop_quit (loop);
61  break;
62  }
63 
64  case GST_MESSAGE_ERROR:
65  {
66  gchar *debug;
67  GError *error;
68 
69  gst_message_parse_error (msg, &error, &debug);
70  g_free (debug);
71 
72  yCError(H264CARRIER, "GSTREAMER: Error: %s", error->message);
73  g_error_free (error);
74 
75  gst_element_set_state (pipeline, GST_STATE_NULL);
76  break;
77  }
78  default:
79  {
80  yCTrace("GSTREAMER: I received message of type %d", GST_MESSAGE_TYPE (msg));
81  break;
82  }
83  }
84 
85  return GST_BUS_PASS;
86 }
87 */
88 
89 static gboolean link_videosrc2nextWithCaps(GstElement *e1, GstElement *e2)
90 {
91  gboolean link_ok;
92  GstCaps *caps;
93 
94 /*
95 // "application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)H264, payload=(int)96, a-framerate=(string)30"
96  caps = gst_caps_new_simple("application/x-rtp",
97  "media", G_TYPE_STRING, "video",
98  "clock-rate", G_TYPE_INT, 90000,
99  "encoding-name", G_TYPE_STRING, "H264",
100  "payload", G_TYPE_INT, 96,
101  "a-framerate", G_TYPE_STRING, "30",
102  NULL);
103 */
104 // "application/x-rtp, media=(string)video, encoding-name=(string)H264, payload=(int)96"
105  caps = gst_caps_new_simple("application/x-rtp",
106  "media", G_TYPE_STRING, "video",
107  "encoding-name", G_TYPE_STRING, "H264",
108  "payload", G_TYPE_INT, 96,
109  NULL);
110 
111 
112  link_ok = gst_element_link_filtered(e1, e2, caps);
113  if(!link_ok)
114  {
115  yCError(H264CARRIER) << "H264Decoder-GSTREAMER: link_videosrc2nextWithCaps failed";
116  }
117  else
118  {
119  yCTrace(H264CARRIER) << "H264Decoder-GSTREAMER: link_videosrc2nextWithCaps OK";
120  }
121 
122  return (link_ok);
123 }
124 
125 
126 
127 static gboolean link_convert2next(GstElement *e1, GstElement *e2)
128 {
129  gboolean link_ok;
130  GstCaps *caps;
131 
132  caps = gst_caps_new_simple("video/x-raw",
133  "format", G_TYPE_STRING, "RGB",
134  NULL);
135 
136 
137  link_ok = gst_element_link_filtered(e1, e2, caps);
138 
139  if(!link_ok)
140  {
141  yCError(H264CARRIER) << "H264Decoder-GSTREAMER: link_convert2next failed";
142  }
143  else
144  {
145  yCTrace(H264CARRIER) << "H264Decoder-GSTREAMER: link_convert2next OK";
146  }
147 
148  return (link_ok);
149 }
150 
151 
152 GstFlowReturn new_sample(GstAppSink *appsink, gpointer user_data)
153 {
154 #ifdef debug_time
155  static bool isFirst = true;
156  double start_time = Time::now();
157  double end_time=0;
158 
159  static double last_call;
160  static double sumOf_timeBetweenCalls = 0;
161  static double sumOf_timeOfNewSampleFunc = 0;
162  static uint32_t count=0;
163  #define MAX_COUNT 100
164 
165 
166  if(!isFirst)
167  sumOf_timeBetweenCalls+=(start_time -last_call);
168 
169  last_call = start_time;
170 
171 
172 #endif
173 
174  auto* dec_data = (data_for_gst_callback*)user_data;
175 
176  GstSample *sample = nullptr;
177  g_signal_emit_by_name (appsink, "pull-sample", &sample, NULL);
178  if(!sample)
179  {
180  yCWarning(H264CARRIER, "GSTREAMER: could not take a sample!");
181  return GST_FLOW_OK;
182  }
183 
184  GstCaps *caps = gst_sample_get_caps (sample);
185  if(!caps)
186  {
187  yCError(H264CARRIER, "GSTREAMER: could not get caps of sample!");
188  return GST_FLOW_ERROR;
189  }
190  GstStructure *struc = gst_caps_get_structure(caps, 0);
191  if(!struc)
192  {
193  yCError(H264CARRIER, "GSTREAMER: could not get struct of caps!");
194  return GST_FLOW_ERROR;
195  }
196  gint width, height;
197  gboolean res;
198  res = gst_structure_get_int(struc, "width", &width);
199  if(!res)
200  {
201  yCError(H264CARRIER, "GSTREAMER: could not get width!");
202  return GST_FLOW_ERROR;
203  }
204 
205  res = gst_structure_get_int(struc, "height", &height);
206  if(!res)
207  {
208  yCError(H264CARRIER, "GSTREAMER: could not get height!");
209  return GST_FLOW_ERROR;
210  }
211  yCTrace(H264CARRIER, "Image has size %d x %d", width, height);
212 
213  GstBuffer *buffer = gst_sample_get_buffer(sample);
214  GstMapInfo map;
215  if(!gst_buffer_map(buffer, &map, GST_MAP_READ))
216  {
217  yCError(H264CARRIER, "GSTREAMER: could not get map!");
218  return GST_FLOW_ERROR;
219  }
220  //HERE I GET MY IMAGE!!!!
221  //DO SOMETHING...
222  //ImageOf<PixelRgb> &yframebuff = yarp_stuff_ptr->yport_ptr->prepare();
223  dec_data->m->lock();
224  dec_data->isNew = true;
225  dec_data->img->resize(width, height);
226 
227  unsigned char *ydata_ptr = dec_data->img->getRawImage();
228  memcpy(ydata_ptr, map.data, width*height*3);
229 
230  dec_data->m->unlock();
231  gst_buffer_unmap(buffer, &map);
232 
233  gst_sample_unref(sample);
234  if(dec_data->isReq)
235  dec_data->s->post();
236 
237 
238 #ifdef debug_time
239  end_time = Time::now();
240  sumOf_timeOfNewSampleFunc += (end_time-start_time);
241  count++;
242  isFirst=false;
243 
244  if(count>=MAX_COUNT)
245  {
247  "On %d times: NewSampleFunc is long %.6f sec and sleeps %.6f sec",
248  MAX_COUNT,
249  (sumOf_timeOfNewSampleFunc/MAX_COUNT),
250  (sumOf_timeBetweenCalls/MAX_COUNT) );
251  count = 0;
252  isFirst = true;
253  sumOf_timeBetweenCalls = 0;
254  sumOf_timeOfNewSampleFunc = 0;
255  }
256 
257 
258 #endif
259 
260 
261  return GST_FLOW_OK;
262 
263 }
264 
265 
266 
267 
268 
269 
270 
271 //----------------------------------------------------------------------
272 
273 
274 
275 
276 
277 
278 
279 
281 {
282 public:
283  //GMainLoop *loop;
284 
285  GstElement *pipeline;
286  GstElement *source;
287  GstElement *sink;
288  GstElement *jitterBuff;
289  GstElement *rtpDepay;
290  GstElement *parser;
291  GstElement *convert;
292  GstElement *decoder;
293  GstElement *sizeChanger;
294 
296 
297  GstBus *bus; //maybe can be moved in function where i use it
299 
301 
302  H264DecoderHelper(std::mutex* m_ptr, Semaphore* s_ptr) :
303  pipeline(nullptr),
304  source(nullptr),
305  sink(nullptr),
306  jitterBuff(nullptr),
307  rtpDepay(nullptr),
308  parser(nullptr),
309  convert(nullptr),
310  decoder(nullptr),
311  sizeChanger(nullptr),
312  bus(nullptr),
313  bus_watch_id(0)
314  {
315  gst_cbk_data.m = m_ptr;
316  gst_cbk_data.img = &myframe;
317  gst_cbk_data.s = s_ptr;
318  }
320 
321 
323  {
324  gst_init(nullptr, nullptr);
325  pipeline = gst_pipeline_new ("video-player");
326  source = gst_element_factory_make ("udpsrc", "video-source");
327  rtpDepay = gst_element_factory_make ("rtph264depay", "rtp-depay");
328  parser = gst_element_factory_make ("h264parse", "parser");
329  decoder = gst_element_factory_make ("avdec_h264", "decoder");
330  sizeChanger = gst_element_factory_make ("videocrop", "cropper");
331  convert = gst_element_factory_make ("videoconvert", "convert"); //because use RGB space
332  sink = gst_element_factory_make ("appsink", "video-output");
333 
334  if (!pipeline || !source || !rtpDepay || !parser || !decoder || !convert || !sink || !sizeChanger)
335  {
336  yCError(H264CARRIER) << "H264Decoder-GSTREAMER: one element could not be created. Exiting.";
337  return false;
338  }
339  if (cfgParams.removeJitter)
340  {
341  jitterBuff = gst_element_factory_make("rtpjitterbuffer", "jitterBuffer");
342  if (!jitterBuff)
343  {
344  yCError(H264CARRIER) << "H264Decoder-GSTREAMER: rtpjitterbuffer could not be created. Exiting.";
345  return false;
346  }
347  }
348 
349  yCTrace(H264CARRIER) << "H264Decoder-GSTREAMER: istantiateElements OK";
350 
351  return true;
352  }
353 
354  bool configureElements(h264Decoder_cfgParamters &cfgParams) //maybe i can make callbak configurable in the future.....
355  {
356  // 1) configure source port
357  yCTrace(H264CARRIER) << "H264Decoder-GSTREAMER: try to configure source port with value" << cfgParams.remotePort;
358  g_object_set(source, "port", cfgParams.remotePort, NULL);
359  yCDebug(H264CARRIER) << "H264Decoder-GSTREAMER: configured source port with" << cfgParams.remotePort;
360 
361  // 2) configure callback on new frame
362  yCTrace(H264CARRIER) << "H264Decoder-GSTREAMER: try to configure appsink.... ";
363  //I decided to use callback mechanism because it should have less overhead
364  g_object_set( sink, "emit-signals", false, NULL );
365 
366  GstAppSinkCallbacks cbs; // Does this need to be kept alive?
367 
368  // Set Video Sink callback methods
369  cbs.eos = nullptr;
370  cbs.new_preroll = nullptr;
371  cbs.new_sample = &new_sample;
372  gst_app_sink_set_callbacks( GST_APP_SINK( sink ), &cbs, &gst_cbk_data, nullptr );
373 
374  /* //3) add watch ( a message handler)
375  bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
376  //bus_watch_id = gst_bus_add_watch (bus, bus_call, loop);
377  gst_object_unref (bus);
378 
379  gst_bus_set_sync_handler(bus, bus_call, pipeline, NULL);
380  gst_object_unref (bus);
381  */
382 
383  //videocrop
384  yCTrace(H264CARRIER) << "H264Decoder-GSTREAMER: try to set new size: left" << cfgParams.crop.left << "right=" << cfgParams.crop.right << "top=" << cfgParams.crop.top << "bottom" << cfgParams.crop.bottom;
385  g_object_set(G_OBJECT(sizeChanger), "left", cfgParams.crop.left, "right", cfgParams.crop.right, "top", cfgParams.crop.top, "bottom", cfgParams.crop.bottom, NULL);
386  yCTrace(H264CARRIER) << "H264Decoder-GSTREAMER: set new size: left" << cfgParams.crop.left << "right=" << cfgParams.crop.right << "top=" << cfgParams.crop.top << "bottom" << cfgParams.crop.bottom;
387 
388  yCTrace(H264CARRIER) << "H264Decoder-GSTREAMER: configureElements OK";
389  return true;
390 
391  }
392 
394  {
395 
396  yCTrace(H264CARRIER) << "H264Decoder-GSTREAMER: try to add elements to pipeline..... ";
397  /* we add all elements into the pipeline */
398  gst_bin_add_many (GST_BIN (pipeline),
399  source, rtpDepay, parser, decoder, sizeChanger, convert, sink, NULL);
400 
401  gboolean result;
402 
403  if (jitterBuff != nullptr)
404  {
405  result = gst_bin_add(GST_BIN(pipeline), jitterBuff);
406  if (!result) { yCError(H264CARRIER) << "H264Decoder: Error adding jitterBuff to the bin"; return false; }
407  }
408 
409  yCTrace(H264CARRIER) << "H264Decoder-GSTREAMER: elements have been added in pipeline!";
410 
411  yCTrace(H264CARRIER) << "H264Decoder-GSTREAMER: try to link_convert2next..... ";
412  result = link_convert2next(convert, sink);
413  if (!result) { yCError(H264CARRIER) << "H264Decoder: Error linking converter to sink "; return false; }
414 
415  /* autovideosrc ! "video/x-raw, width=640, height=480, format=(string)I420" ! videoconvert ! 'video/x-raw, format=(string)RGB' ! yarpdevice ! glimagesink */
416 
417  if (jitterBuff)
418  {
419  yCTrace(H264CARRIER) << "H264Decoder-GSTREAMER: try to link videosrc to rtpjitterBuffer.....";
420  result = link_videosrc2nextWithCaps(source, jitterBuff);
421  if (!result){ yCError(H264CARRIER) << "H264Decoder: Error linking videosrc to rtpjitterBuffer "; return false;}
422 
423  yCTrace(H264CARRIER) << "H264Decoder-GSTREAMER: try to link jitterBuff to rtpDapay.....";
424  result = gst_element_link(jitterBuff, rtpDepay);
425  if (!result) { yCError(H264CARRIER) << "H264Decoder: Error linking jitterBuff to rtpDapay "; return false; }
426 
427  }
428  else
429  {
430  yCTrace(H264CARRIER) << "H264Decoder-GSTREAMER: try to videosrc to rtpDepay";
431  result = link_videosrc2nextWithCaps(source, rtpDepay);
432  if (!result) { yCError(H264CARRIER) << "H264Decoder: Error linking videosrc to rtpDepay "; return false; }
433 
434  }
435 
436  yCTrace(H264CARRIER) << "H264Decoder-GSTREAMER: try to link all other elements.....";
437  gst_element_link_many(rtpDepay, parser, decoder, sizeChanger, convert, NULL);
438 
439  yCTrace(H264CARRIER) << "H264Decoder-GSTREAMER: linkElements OK";
440  return true;
441  }
442 
443 
444 };
445 
446 
447 
448 #define GET_HELPER(x) (*((H264DecoderHelper*)(x)))
449 
451  sysResource(new H264DecoderHelper(&mutex, &semaphore)),
452  cfg(config)
453 {
454 }
455 
457 {
458  H264DecoderHelper &helper = GET_HELPER(sysResource);
459  if(!helper.istantiateElements(cfg))
460  {
461  yCError(H264CARRIER) << "H264Decoder: Error in istantiateElements";
462  return false;
463  }
464 
465  if(!helper.configureElements(cfg))
466  {
467  yCError(H264CARRIER) << "Error in configureElements";
468  return false;
469  }
470 
471  if(!helper.linkElements())
472  {
473  yCError(H264CARRIER) << "Error in linkElements";
474  return false;
475  }
476 
477  yCDebug(H264CARRIER) << "H264Decoder-GSTREAMER: init ok";
478  return true;
479 
480 }
481 
482 
484 {
485  H264DecoderHelper &helper = GET_HELPER(sysResource);
486  gst_element_set_state (helper.pipeline, GST_STATE_PLAYING);
487  yCDebug(H264CARRIER) << "H264Decoder: pipeline started!";
488 
489  return true;
490 
491 }
492 
494 {
495  H264DecoderHelper &helper = GET_HELPER(sysResource);
496  gst_element_set_state (helper.pipeline, GST_STATE_NULL);
497  gst_bus_set_sync_handler(gst_pipeline_get_bus (GST_PIPELINE (helper.pipeline)), nullptr, nullptr, nullptr);
498  yCDebug(H264CARRIER) << "H264Decoder: deleting pipeline";
499  gst_object_unref (GST_OBJECT (helper.pipeline));
500  return true;
501 }
502 
504 {
505  stop();
506  delete &GET_HELPER(sysResource);
507 
508 
509 }
510 
512 {
513  H264DecoderHelper &helper = GET_HELPER(sysResource);
514  helper.gst_cbk_data.isNew = false;
515  helper.gst_cbk_data.isReq = false;
516  return helper.myframe;
517 }
518 
520 {
521  H264DecoderHelper &helper = GET_HELPER(sysResource);
522  return helper.gst_cbk_data.isNew;
523 }
524 
526 {
527  H264DecoderHelper &helper = GET_HELPER(sysResource);
528  return (helper.myframe.width() * helper.myframe.height() * 3);
529 }
530 
532 {
533  H264DecoderHelper &helper = GET_HELPER(sysResource);
534  helper.gst_cbk_data.isReq = true;
535 
536 }
LogStream.h
h264Decoder_cfgParamters::remotePort
int remotePort
Definition: H264Decoder.h:33
H264DecoderHelper::convert
GstElement * convert
Definition: H264Decoder.cpp:291
H264DecoderHelper::~H264DecoderHelper
~H264DecoderHelper()
Definition: H264Decoder.cpp:319
H264Decoder::getLastFrameSize
int getLastFrameSize()
Definition: H264Decoder.cpp:525
H264DecoderHelper::linkElements
bool linkElements()
Definition: H264Decoder.cpp:393
H264DecoderHelper::bus_watch_id
guint bus_watch_id
Definition: H264Decoder.cpp:298
H264DecoderHelper::gst_cbk_data
data_for_gst_callback gst_cbk_data
Definition: H264Decoder.cpp:295
yarp::os::Semaphore
A class for thread synchronization and mutual exclusion.
Definition: Semaphore.h:29
yarp::sig
Signal processing.
Definition: Image.h:25
H264Decoder.h
yCWarning
#define yCWarning(component,...)
Definition: LogComponent.h:146
H264Decoder::start
bool start()
Definition: H264Decoder.cpp:483
data_for_gst_callback::img
ImageOf< PixelRgb > * img
Definition: H264Decoder.cpp:39
data_for_gst_callback::m
std::mutex * m
Definition: H264Decoder.cpp:38
H264Decoder::setReq
void setReq()
Definition: H264Decoder.cpp:531
H264DecoderHelper::H264DecoderHelper
H264DecoderHelper(std::mutex *m_ptr, Semaphore *s_ptr)
Definition: H264Decoder.cpp:302
H264DecoderHelper::configureElements
bool configureElements(h264Decoder_cfgParamters &cfgParams)
Definition: H264Decoder.cpp:354
new_sample
GstFlowReturn new_sample(GstAppSink *appsink, gpointer user_data)
Definition: H264Decoder.cpp:152
H264DecoderHelper::istantiateElements
bool istantiateElements(h264Decoder_cfgParamters &cfgParams)
Definition: H264Decoder.cpp:322
data_for_gst_callback::isReq
bool isReq
Definition: H264Decoder.cpp:42
h264Decoder_cfgParamters::top
int top
Definition: H264Decoder.h:28
H264DecoderHelper::myframe
ImageOf< PixelRgb > myframe
Definition: H264Decoder.cpp:300
yarp::os::Time::now
double now()
Return the current time in seconds, relative to an arbitrary starting point.
Definition: Time.cpp:124
yarp::sig::ImageOf< PixelRgb >
data_for_gst_callback
Definition: H264Decoder.cpp:35
data_for_gst_callback::isNew
bool isNew
Definition: H264Decoder.cpp:40
H264DecoderHelper::parser
GstElement * parser
Definition: H264Decoder.cpp:290
H264Decoder::stop
bool stop()
Definition: H264Decoder.cpp:493
h264Decoder_cfgParamters::right
int right
Definition: H264Decoder.h:27
H264DecoderHelper::sizeChanger
GstElement * sizeChanger
Definition: H264Decoder.cpp:293
H264DecoderHelper::jitterBuff
GstElement * jitterBuff
Definition: H264Decoder.cpp:288
H264DecoderHelper::bus
GstBus * bus
Definition: H264Decoder.cpp:297
h264Decoder_cfgParamters
Definition: H264Decoder.h:17
h264Decoder_cfgParamters::crop
struct h264Decoder_cfgParamters::@0 crop
H264DecoderHelper::rtpDepay
GstElement * rtpDepay
Definition: H264Decoder.cpp:289
buffer
Definition: V4L_camera.h:75
H264LogComponent.h
H264DecoderHelper
Definition: H264Decoder.cpp:281
H264DecoderHelper::decoder
GstElement * decoder
Definition: H264Decoder.cpp:292
data_for_gst_callback::data_for_gst_callback
data_for_gst_callback()=default
GET_HELPER
#define GET_HELPER(x)
Definition: H264Decoder.cpp:448
yCError
#define yCError(component,...)
Definition: LogComponent.h:157
H264Decoder::init
bool init()
Definition: H264Decoder.cpp:456
H264DecoderHelper::pipeline
GstElement * pipeline
Definition: H264Decoder.cpp:285
H264DecoderHelper::sink
GstElement * sink
Definition: H264Decoder.cpp:287
h264Decoder_cfgParamters::bottom
int bottom
Definition: H264Decoder.h:29
yarp::os
An interface to the operating system, including Port based communication.
Definition: AbstractCarrier.h:17
yCDebug
#define yCDebug(component,...)
Definition: LogComponent.h:112
link_videosrc2nextWithCaps
static gboolean link_videosrc2nextWithCaps(GstElement *e1, GstElement *e2)
Definition: H264Decoder.cpp:89
Time.h
yCTrace
#define yCTrace(component,...)
Definition: LogComponent.h:88
H264Decoder::H264Decoder
H264Decoder(h264Decoder_cfgParamters &config)
Definition: H264Decoder.cpp:450
data_for_gst_callback::s
Semaphore * s
Definition: H264Decoder.cpp:41
H264DecoderHelper::source
GstElement * source
Definition: H264Decoder.cpp:286
h264Decoder_cfgParamters::removeJitter
bool removeJitter
Definition: H264Decoder.h:34
H264Decoder::~H264Decoder
~H264Decoder()
Definition: H264Decoder.cpp:503
H264CARRIER
const yarp::os::LogComponent & H264CARRIER()
Definition: H264LogComponent.cpp:16
H264Decoder::newFrameIsAvailable
bool newFrameIsAvailable()
Definition: H264Decoder.cpp:519
h264Decoder_cfgParamters::left
int left
Definition: H264Decoder.h:26
link_convert2next
static gboolean link_convert2next(GstElement *e1, GstElement *e2)
Definition: H264Decoder.cpp:127
H264Decoder::getLastFrame
yarp::sig::ImageOf< yarp::sig::PixelRgb > & getLastFrame()
Definition: H264Decoder.cpp:511