decoding_subscribers.hpp
Go to the documentation of this file.
1 #ifndef CODEC_IMAGE_TRANSPORT_DECODING_SUBSCRIBERS_HPP
2 #define CODEC_IMAGE_TRANSPORT_DECODING_SUBSCRIBERS_HPP
3 
4 #include <string>
5 
8 #include <ros/console.h>
9 #include <ros/exception.h>
10 #include <ros/node_handle.h>
11 #include <sensor_msgs/CompressedImage.h>
12 #include <sensor_msgs/Image.h>
14 
15 #include <boost/mpl/string.hpp>
16 #include <boost/shared_ptr.hpp>
17 
18 extern "C" {
19 #include <libavcodec/avcodec.h>
20 #include <libavutil/imgutils.h>
21 #include <libswscale/swscale.h>
22 }
23 
25 
26 template < AVCodecID CodecID, typename TransportName >
28  : public image_transport::SimpleSubscriberPlugin< sensor_msgs::CompressedImage > {
29 public:
31 
32  virtual ~DecodingSubscriber() {}
33 
34  virtual std::string getTransportName() const { return boost::mpl::c_str< TransportName >::value; }
35 
36 private:
37  virtual void subscribeImpl(ros::NodeHandle &nh, const std::string &base_topic,
38  uint32_t queue_size, const Callback &callback,
39  const ros::VoidPtr &tracked_object,
40  const image_transport::TransportHints &transport_hints) {
41  if (!decoder_ctx_) {
42  // init libavformat
43  avcodec_register_all();
44  av_log_set_level(AV_LOG_FATAL);
45 
46  // find a decoder
47  AVCodec *const decoder(avcodec_find_decoder(CodecID));
48  if (!decoder) {
49  throw ros::Exception("Cannot find a decoder");
50  }
51 
52  // allocate a decoder context
53  decoder_ctx_.reset(avcodec_alloc_context3(decoder), deleteAVCodecContext);
54  if (!decoder_ctx_) {
55  throw ros::Exception("Cannot allocate a decoder context");
56  }
57 
58  // open decoder
59  if (avcodec_open2(decoder_ctx_.get(), decoder, NULL) < 0) {
60  throw ros::Exception("Failed to open codec");
61  }
62  }
63 
64  Base::subscribeImpl(nh, base_topic, queue_size, callback, tracked_object, transport_hints);
65  }
66 
67  virtual void internalCallback(const sensor_msgs::CompressedImage::ConstPtr &message,
68  const Callback &user_cb) {
69  // set packet data from the input message
70  AVPacket packet;
71  av_init_packet(&packet);
72  packet.size = message->data.size();
73  packet.data = const_cast< uint8_t * >(&message->data[0]);
74 
75  // send the packet to the decoder
76  if (avcodec_send_packet(decoder_ctx_.get(), &packet) < 0) {
77  ROS_ERROR("Cannot send a packet to decoder");
78  return;
79  }
80 
81  while (true) {
82  // allocate a frame for decoded data
83  boost::shared_ptr< AVFrame > frame(av_frame_alloc(), deleteAVFrame);
84  if (!frame) {
85  ROS_ERROR("Cannot allocate frame");
86  return;
87  }
88 
89  // receive the decoded data from the decoder
90  const int res(avcodec_receive_frame(decoder_ctx_.get(), frame.get()));
91  if (res == AVERROR(EAGAIN) || res == AVERROR_EOF) {
92  // no more frames in the packet
93  return;
94  } else if (res < 0) {
95  ROS_ERROR("Cannot receive a frame");
96  return;
97  }
98 
99  // allocate output message
100  const sensor_msgs::ImagePtr out(new sensor_msgs::Image());
101  out->header = message->header;
102  out->height = frame->height;
103  out->width = frame->width;
104  out->encoding = sensor_msgs::image_encodings::BGR8;
105  out->step = 3 * frame->width;
106  out->data.resize(3 * frame->width * frame->height);
107 
108  // layout data by converting color spaces (YUV -> RGB)
110  sws_getContext(
111  // src formats
112  frame->width, frame->height,
113  toUndeprecated(static_cast< AVPixelFormat >(frame->format)),
114  // dst formats
115  frame->width, frame->height, AV_PIX_FMT_BGR24,
116  // flags & filters
117  SWS_FAST_BILINEAR, NULL, NULL, NULL),
118  sws_freeContext);
119  int stride(out->step);
120  uint8_t *dst(&out->data[0]);
121  sws_scale(convert_ctx.get(),
122  // src data
123  frame->data, frame->linesize, 0, frame->height,
124  // dst data
125  &dst, &stride);
126 
127  // exec user callback
128  user_cb(out);
129  }
130  }
131 
132  // Deleter for auto free/close of libav objects
133  static void deleteAVFrame(AVFrame *frame) {
134  if (frame) {
135  av_frame_free(&frame);
136  }
137  }
138 
139  static void deleteAVCodecContext(AVCodecContext *ctx) {
140  if (ctx) {
141  avcodec_free_context(&ctx);
142  }
143  }
144 
145  // Workaround to avoid deprecated pixel format warning
146  static AVPixelFormat toUndeprecated(const AVPixelFormat format) {
147  switch (format) {
148  case AV_PIX_FMT_YUVJ420P:
149  return AV_PIX_FMT_YUV420P;
150  case AV_PIX_FMT_YUVJ411P:
151  return AV_PIX_FMT_YUV411P;
152  case AV_PIX_FMT_YUVJ422P:
153  return AV_PIX_FMT_YUV422P;
154  case AV_PIX_FMT_YUVJ440P:
155  return AV_PIX_FMT_YUV440P;
156  case AV_PIX_FMT_YUVJ444P:
157  return AV_PIX_FMT_YUV444P;
158  default:
159  return format;
160  }
161  }
162 
163 private:
165 
167 };
168 
171 
172 } // namespace decoding_image_transport
173 
174 #endif
static void deleteAVCodecContext(AVCodecContext *ctx)
DecodingSubscriber< AV_CODEC_ID_H264, boost::mpl::string< 'h', '2', '6', '4' > > H264Subscriber
boost::function< void(const sensor_msgs::ImageConstPtr &)> Callback
virtual void subscribeImpl(ros::NodeHandle &nh, const std::string &base_topic, uint32_t queue_size, const Callback &callback, const ros::VoidPtr &tracked_object, const image_transport::TransportHints &transport_hints)
virtual void internalCallback(const sensor_msgs::CompressedImage::ConstPtr &message, const Callback &user_cb)
static AVPixelFormat toUndeprecated(const AVPixelFormat format)
image_transport::SimpleSubscriberPlugin< sensor_msgs::CompressedImage > Base
boost::shared_ptr< AVCodecContext > decoder_ctx_
#define ROS_ERROR(...)
virtual void subscribeImpl(ros::NodeHandle &nh, const std::string &base_topic, uint32_t queue_size, const Callback &callback, const ros::VoidPtr &tracked_object, const TransportHints &transport_hints)


codec_image_transport
Author(s):
autogenerated on Mon Feb 28 2022 22:10:37