theora_subscriber.cpp
Go to the documentation of this file.
1 /*********************************************************************
2 * Software License Agreement (BSD License)
3 *
4 * Copyright (c) 20012, Willow Garage, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
17 * * Neither the name of the Willow Garage nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *********************************************************************/
34 
36 #include <cv_bridge/cv_bridge.h>
38 #include <opencv2/imgproc/imgproc.hpp>
39 #include <boost/scoped_array.hpp>
40 #include <vector>
41 
42 using namespace std;
43 
44 namespace theora_image_transport {
45 
46 TheoraSubscriber::TheoraSubscriber()
47  : pplevel_(0),
48  received_header_(false),
49  received_keyframe_(false),
50  decoding_context_(NULL),
51  setup_info_(NULL)
52 {
53  th_info_init(&header_info_);
54  th_comment_init(&header_comment_);
55 }
56 
58 {
59  if (decoding_context_) th_decode_free(decoding_context_);
60  th_setup_free(setup_info_);
61  th_info_clear(&header_info_);
62  th_comment_clear(&header_comment_);
63 }
64 
65 void TheoraSubscriber::subscribeImpl(ros::NodeHandle &nh, const std::string &base_topic, uint32_t queue_size,
66  const Callback &callback, const ros::VoidPtr &tracked_object,
67  const image_transport::TransportHints &transport_hints)
68 {
69  // queue_size doesn't account for the 3 header packets, so we correct (with a little extra) here.
70  queue_size += 4;
72  Base::subscribeImpl(nh, base_topic, queue_size, callback, tracked_object, transport_hints);
73 
74  // Set up reconfigure server for this topic
75  reconfigure_server_ = boost::make_shared<ReconfigureServer>(this->nh());
76  ReconfigureServer::CallbackType f = boost::bind(&TheoraSubscriber::configCb, this, _1, _2);
77  reconfigure_server_->setCallback(f);
78 }
79 
80 void TheoraSubscriber::configCb(Config& config, uint32_t level)
81 {
82  if (decoding_context_ && pplevel_ != config.post_processing_level) {
83  pplevel_ = updatePostProcessingLevel(config.post_processing_level);
84  config.post_processing_level = pplevel_; // In case more than PPLEVEL_MAX
85  }
86  else
87  pplevel_ = config.post_processing_level;
88 }
89 
91 {
92  int pplevel_max;
93  int err = th_decode_ctl(decoding_context_, TH_DECCTL_GET_PPLEVEL_MAX, &pplevel_max, sizeof(int));
94  if (err)
95  ROS_WARN("Failed to get maximum post-processing level, error code %d", err);
96  else if (level > pplevel_max) {
97  ROS_WARN("Post-processing level %d is above the maximum, clamping to %d", level, pplevel_max);
98  level = pplevel_max;
99  }
100 
101  err = th_decode_ctl(decoding_context_, TH_DECCTL_SET_PPLEVEL, &level, sizeof(int));
102  if (err) {
103  ROS_ERROR("Failed to set post-processing level, error code %d", err);
104  return pplevel_; // old value
105  }
106  return level;
107 }
108 
109 //When using this caller is responsible for deleting oggpacket.packet!!
110 void TheoraSubscriber::msgToOggPacket(const theora_image_transport::Packet &msg, ogg_packet &ogg)
111 {
112  ogg.bytes = msg.data.size();
113  ogg.b_o_s = msg.b_o_s;
114  ogg.e_o_s = msg.e_o_s;
115  ogg.granulepos = msg.granulepos;
116  ogg.packetno = msg.packetno;
117  ogg.packet = new unsigned char[ogg.bytes];
118  memcpy(ogg.packet, &msg.data[0], ogg.bytes);
119 }
120 
121 void TheoraSubscriber::internalCallback(const theora_image_transport::PacketConstPtr& message, const Callback& callback)
122 {
124  ogg_packet oggpacket;
125  msgToOggPacket(*message, oggpacket);
126  boost::scoped_array<unsigned char> packet_guard(oggpacket.packet); // Make sure packet memory gets deleted
127 
128  // Beginning of logical stream flag means we're getting new headers
129  if (oggpacket.b_o_s == 1) {
130  // Clear all state, everything we knew is wrong
131  received_header_ = false;
132  received_keyframe_ = false;
133  if (decoding_context_) {
134  th_decode_free(decoding_context_);
135  decoding_context_ = NULL;
136  }
137  th_setup_free(setup_info_);
138  setup_info_ = NULL;
139  th_info_clear(&header_info_);
140  th_info_init(&header_info_);
141  th_comment_clear(&header_comment_);
142  th_comment_init(&header_comment_);
143  latest_image_.reset();
144  }
145 
146  // Decode header packets until we get the first video packet
147  if (received_header_ == false) {
148  int rval = th_decode_headerin(&header_info_, &header_comment_, &setup_info_, &oggpacket);
149  switch (rval) {
150  case 0:
151  // We've received the full header; this is the first video packet.
152  decoding_context_ = th_decode_alloc(&header_info_, setup_info_);
153  if (!decoding_context_) {
154  ROS_ERROR("[theora] Decoding parameters were invalid");
155  return;
156  }
157  received_header_ = true;
159  break; // Continue on the video decoding
160  case TH_EFAULT:
161  ROS_WARN("[theora] EFAULT when processing header packet");
162  return;
163  case TH_EBADHEADER:
164  ROS_WARN("[theora] Bad header packet");
165  return;
166  case TH_EVERSION:
167  ROS_WARN("[theora] Header packet not decodable with this version of libtheora");
168  return;
169  case TH_ENOTFORMAT:
170  ROS_WARN("[theora] Packet was not a Theora header");
171  return;
172  default:
173  // If rval > 0, we successfully received a header packet.
174  if (rval < 0)
175  ROS_WARN("[theora] Error code %d when processing header packet", rval);
176  return;
177  }
178  }
179 
180  // Wait for a keyframe if we haven't received one yet - delta frames are useless to us in that case
181  received_keyframe_ = received_keyframe_ || (th_packet_iskeyframe(&oggpacket) == 1);
182  if (!received_keyframe_)
183  return;
184 
185  // We have a video packet we can handle, let's decode it
186  int rval = th_decode_packetin(decoding_context_, &oggpacket, NULL);
187  switch (rval) {
188  case 0:
189  break; // Yay, we got a frame. Carry on below.
190  case TH_DUPFRAME:
191  // Video data hasn't changed, so we update the timestamp and reuse the last received frame.
192  ROS_DEBUG("[theora] Got a duplicate frame");
193  if (latest_image_) {
194  latest_image_->header = message->header;
195  callback(latest_image_);
196  }
197  return;
198  case TH_EFAULT:
199  ROS_WARN("[theora] EFAULT processing video packet");
200  return;
201  case TH_EBADPACKET:
202  ROS_WARN("[theora] Packet does not contain encoded video data");
203  return;
204  case TH_EIMPL:
205  ROS_WARN("[theora] The video data uses bitstream features not supported by this version of libtheora");
206  return;
207  default:
208  ROS_WARN("[theora] Error code %d when decoding video packet", rval);
209  return;
210  }
211 
212  // We have a new decoded frame available
213  th_ycbcr_buffer ycbcr_buffer;
214  th_decode_ycbcr_out(decoding_context_, ycbcr_buffer);
215 
216  // Wrap YCbCr channel data into OpenCV format
217  th_img_plane &y_plane = ycbcr_buffer[0], &cb_plane = ycbcr_buffer[1], &cr_plane = ycbcr_buffer[2];
218  cv::Mat y(y_plane.height, y_plane.width, CV_8UC1, y_plane.data, y_plane.stride);
219  cv::Mat cb_sub(cb_plane.height, cb_plane.width, CV_8UC1, cb_plane.data, cb_plane.stride);
220  cv::Mat cr_sub(cr_plane.height, cr_plane.width, CV_8UC1, cr_plane.data, cr_plane.stride);
221 
222  // Upsample chroma channels
223  cv::Mat cb, cr;
224  cv::pyrUp(cb_sub, cb);
225  cv::pyrUp(cr_sub, cr);
226 
227  // Merge into interleaved image. Note OpenCV uses YCrCb, so we swap the chroma channels.
228  cv::Mat ycrcb, channels[] = {y, cr, cb};
229  cv::merge(channels, 3, ycrcb);
230 
231  // Convert to BGR color
232  cv::Mat bgr, bgr_padded;
233  cv::cvtColor(ycrcb, bgr_padded, CV_YCrCb2BGR);
234  // Pull out original (non-padded) image region
235  bgr = bgr_padded(cv::Rect(header_info_.pic_x, header_info_.pic_y,
236  header_info_.pic_width, header_info_.pic_height));
237 
240  callback(latest_image_);
241 }
242 
243 } //namespace theora_image_transport
f
theora_image_transport::TheoraSubscriberConfig Config
boost::function< void(const sensor_msgs::ImageConstPtr &)> Callback
#define ROS_WARN(...)
void msgToOggPacket(const theora_image_transport::Packet &msg, ogg_packet &ogg)
boost::shared_ptr< ReconfigureServer > reconfigure_server_
virtual void internalCallback(const theora_image_transport::PacketConstPtr &msg, const Callback &user_cb)
#define ROS_ERROR(...)
void configCb(Config &config, uint32_t level)
sensor_msgs::ImagePtr toImageMsg() const
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)
#define ROS_DEBUG(...)


theora_image_transport
Author(s): Patrick Mihelich, Ethan Dreyfuss
autogenerated on Fri Sep 20 2019 03:32:16