00001 #include "theora_imagem_transport/theora_publisher.h"
00002 #include <sensor_msgs/image_encodings.h>
00003 #include <opencv/highgui.h>
00004
00005 #include <sensor_msgs/image_encodings.h>
00006 #include <std_msgs/Header.h>
00007
00008 #include <vector>
00009 #include <cstdio>
00010
00011 using namespace std;
00012
00013 namespace tit = theora_image_transport;
00014
00015 namespace theora_imagem_transport
00016 {
00017
00018 TheoraPublisher::TheoraPublisher()
00019 {
00020
00021 th_info_init(&encoder_setup_);
00022
00023 encoder_setup_.pic_x = 0;
00024 encoder_setup_.pic_y = 0;
00025 encoder_setup_.colorspace = TH_CS_UNSPECIFIED;
00026 encoder_setup_.pixel_fmt = TH_PF_420;
00027 encoder_setup_.aspect_numerator = 1;
00028 encoder_setup_.aspect_denominator = 1;
00029 encoder_setup_.fps_numerator = 1;
00030 encoder_setup_.fps_denominator = 1;
00031 encoder_setup_.keyframe_granule_shift = 6;
00032
00033 encoder_setup_.target_bitrate = -1;
00034 encoder_setup_.quality = -1;
00035 }
00036
00037 TheoraPublisher::~TheoraPublisher()
00038 {
00039 th_info_clear(&encoder_setup_);
00040 }
00041
00042
00043 void TheoraPublisher::advertiseImpl(ros::NodeHandle & nh, const std::string& base_topic, uint32_t queue_size,
00044 const message_transport::SingleSubscriberPublisher<sensor_msgs::Image>::StatusCB& user_connect_cb,
00045 const message_transport::SingleSubscriberPublisher<sensor_msgs::Image>::StatusCB& user_disconnect_cb,
00046 const ros::VoidPtr& tracked_object, bool latch)
00047 {
00048
00049 queue_size += 4;
00050
00051
00052 latch = false;
00053 typedef message_transport::SimplePublisherPlugin<sensor_msgs::Image,theora_image_transport::Packet> Base;
00054 Base::advertiseImpl(nh, base_topic, queue_size, user_connect_cb, user_disconnect_cb, tracked_object, latch);
00055
00056
00057 reconfigure_server_ = boost::make_shared<ReconfigureServer>(this->getParamNode());
00058 ReconfigureServer::CallbackType f = boost::bind(&TheoraPublisher::configCb, this, _1, _2);
00059 reconfigure_server_->setCallback(f);
00060 }
00061
00062 void TheoraPublisher::configCb(Config& config, uint32_t level)
00063 {
00064
00065 long bitrate = 0;
00066 if (config.optimize_for == theora_imagem_transport::TheoraPublisher_Bitrate)
00067 bitrate = config.target_bitrate;
00068 bool update_bitrate = bitrate && encoder_setup_.target_bitrate != bitrate;
00069 bool update_quality = !bitrate && ((encoder_setup_.quality != config.quality) || encoder_setup_.target_bitrate > 0);
00070 encoder_setup_.quality = config.quality;
00071 encoder_setup_.target_bitrate = bitrate;
00072 keyframe_frequency_ = config.keyframe_frequency;
00073
00074 if (encoding_context_) {
00075 int err = 0;
00076
00077 #ifdef TH_ENCCTL_SET_BITRATE
00078 if (update_bitrate) {
00079 err = th_encode_ctl(encoding_context_.get(), TH_ENCCTL_SET_BITRATE, &bitrate, sizeof(long));
00080 if (err)
00081 ROS_ERROR("Failed to update bitrate dynamically");
00082 }
00083 #else
00084 err |= update_bitrate;
00085 #endif
00086
00087 #ifdef TH_ENCCTL_SET_QUALITY
00088 if (update_quality) {
00089 err = th_encode_ctl(encoding_context_.get(), TH_ENCCTL_SET_QUALITY, &config.quality, sizeof(int));
00090
00091
00092 if (err && err != TH_EINVAL)
00093 ROS_ERROR("Failed to update quality dynamically");
00094 }
00095 #else
00096 err |= update_quality;
00097 #endif
00098
00099
00100 if (err) {
00101 encoding_context_.reset();
00102 }
00103
00104 else {
00105 updateKeyframeFrequency();
00106 config.keyframe_frequency = keyframe_frequency_;
00107 }
00108 }
00109 }
00110
00111 void TheoraPublisher::connectCallback(const ros::SingleSubscriberPublisher& pub)
00112 {
00113
00114 for (unsigned int i = 0; i < stream_header_.size(); i++) {
00115 pub.publish(stream_header_[i]);
00116 }
00117 }
00118
00119 static void cvToTheoraPlane(cv::Mat& mat, th_img_plane& plane)
00120 {
00121 plane.width = mat.cols;
00122 plane.height = mat.rows;
00123 plane.stride = mat.step;
00124 plane.data = mat.data;
00125 }
00126
00127 void TheoraPublisher::publish(const sensor_msgs::Image& message,
00128 const message_transport::SimplePublisherPlugin<sensor_msgs::Image,theora_image_transport::Packet>::PublishFn& publish_fn) const
00129 {
00130 if (!ensureEncodingContext(message, publish_fn))
00131 return;
00132
00133
00134
00135 boost::shared_ptr<int> tracked_object(new int(0));
00136
00140 cv_bridge::CvImageConstPtr cv_image_ptr = cv_bridge::toCvShare(message,tracked_object, "bgr8");
00141 if (!cv_image_ptr) {
00142 ROS_ERROR("Unable to convert from '%s' to bgr8", message.encoding.c_str());
00143 return;
00144 }
00145
00146 const cv::Mat bgr = cv_image_ptr->image;
00147 cv::Mat bgr_padded;
00148 int frame_width = encoder_setup_.frame_width, frame_height = encoder_setup_.frame_height;
00149 if (frame_width == bgr.cols && frame_height == bgr.rows) {
00150 bgr_padded = bgr;
00151 }
00152 else {
00153 bgr_padded = cv::Mat::zeros(frame_height, frame_width, bgr.type());
00154 cv::Mat pic_roi = bgr_padded(cv::Rect(0, 0, bgr.cols, bgr.rows));
00155 bgr.copyTo(pic_roi);
00156 }
00157
00158
00159 cv::Mat ycrcb;
00160 cv::cvtColor(bgr_padded, ycrcb, CV_BGR2YCrCb);
00161
00162
00163 cv::Mat ycrcb_planes[3];
00164 cv::split(ycrcb, ycrcb_planes);
00165
00166
00167 cv::Mat y = ycrcb_planes[0], cr, cb;
00168 cv::pyrDown(ycrcb_planes[1], cr);
00169 cv::pyrDown(ycrcb_planes[2], cb);
00170
00171
00172 th_ycbcr_buffer ycbcr_buffer;
00173 cvToTheoraPlane(y, ycbcr_buffer[0]);
00174 cvToTheoraPlane(cb, ycbcr_buffer[1]);
00175 cvToTheoraPlane(cr, ycbcr_buffer[2]);
00176
00177
00178 int rval = th_encode_ycbcr_in(encoding_context_.get(), ycbcr_buffer);
00179 if (rval == TH_EFAULT) {
00180 ROS_ERROR("[theora] EFAULT in submitting uncompressed frame to encoder");
00181 return;
00182 }
00183 if (rval == TH_EINVAL) {
00184 ROS_ERROR("[theora] EINVAL in submitting uncompressed frame to encoder");
00185 return;
00186 }
00187
00188
00189 ogg_packet oggpacket;
00190 theora_image_transport::Packet output;
00191 while ((rval = th_encode_packetout(encoding_context_.get(), 0, &oggpacket)) > 0) {
00192 oggPacketToMsg(message.header, oggpacket, output);
00193 publish_fn(output);
00194 }
00195 if (rval == TH_EFAULT)
00196 ROS_ERROR("[theora] EFAULT in retrieving encoded video data packets");
00197 }
00198
00199 void freeContext(th_enc_ctx* context)
00200 {
00201 if (context) th_encode_free(context);
00202 }
00203
00204 bool TheoraPublisher::ensureEncodingContext(const sensor_msgs::Image& image, const PublishFn& publish_fn) const
00205 {
00207 if (encoding_context_ && encoder_setup_.pic_width == image.width &&
00208 encoder_setup_.pic_height == image.height)
00209 return true;
00210
00211
00212
00213 encoder_setup_.frame_width = (image.width + 15) & ~0xF;
00214 encoder_setup_.frame_height = (image.height + 15) & ~0xF;
00215 encoder_setup_.pic_width = image.width;
00216 encoder_setup_.pic_height = image.height;
00217
00218
00219 encoding_context_.reset(th_encode_alloc(&encoder_setup_), freeContext);
00220 if (!encoding_context_) {
00221 ROS_ERROR("[theora] Failed to create encoding context");
00222 return false;
00223 }
00224
00225 updateKeyframeFrequency();
00226
00227 th_comment comment;
00228 th_comment_init(&comment);
00229 boost::shared_ptr<th_comment> clear_guard(&comment, th_comment_clear);
00231 comment.vendor = strdup("Willow Garage theora_image_transport");
00232
00233
00235 stream_header_.clear();
00236 ogg_packet oggpacket;
00237 while (th_encode_flushheader(encoding_context_.get(), &comment, &oggpacket) > 0) {
00238 stream_header_.push_back(theora_image_transport::Packet());
00239 oggPacketToMsg(image.header, oggpacket, stream_header_.back());
00240 publish_fn(stream_header_.back());
00241 }
00242 return true;
00243 }
00244
00245 void TheoraPublisher::oggPacketToMsg(const std_msgs::Header& header, const ogg_packet &oggpacket,
00246 theora_image_transport::Packet &msg) const
00247 {
00248 msg.header = header;
00249 msg.b_o_s = oggpacket.b_o_s;
00250 msg.e_o_s = oggpacket.e_o_s;
00251 msg.granulepos = oggpacket.granulepos;
00252 msg.packetno = oggpacket.packetno;
00253 msg.data.resize(oggpacket.bytes);
00254 memcpy(&msg.data[0], oggpacket.packet, oggpacket.bytes);
00255 }
00256
00257 void TheoraPublisher::updateKeyframeFrequency() const
00258 {
00259 ogg_uint32_t desired_frequency = keyframe_frequency_;
00260 if (th_encode_ctl(encoding_context_.get(), TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE,
00261 &keyframe_frequency_, sizeof(ogg_uint32_t)))
00262 ROS_ERROR("Failed to change keyframe frequency");
00263 if (keyframe_frequency_ != desired_frequency)
00264 ROS_WARN("Couldn't set keyframe frequency to %d, actually set to %d",
00265 desired_frequency, keyframe_frequency_);
00266 }
00267
00268
00269 }