bebop_video_decoder.cpp
Go to the documentation of this file.
00001 
00025 #include "bebop_driver/bebop_video_decoder.h"
00026 
00027 #include <stdexcept>
00028 #include <algorithm>
00029 #include <string>
00030 
00031 #include <boost/lexical_cast.hpp>
00032 
00033 extern "C"
00034 {
00035   #include "libARSAL/ARSAL_Print.h"
00036 }
00037 
00038 namespace bebop_driver
00039 {
00040 
00041 const char* VideoDecoder::LOG_TAG = "Decoder";
00042 
00043 // TODO(mani-monaj): Move to util, inline
00044 void VideoDecoder::ThrowOnCondition(const bool cond, const std::string &message)
00045 {
00046   if (!cond) return;
00047   throw std::runtime_error(message);
00048 }
00049 
00050 VideoDecoder::VideoDecoder()
00051   : codec_initialized_(false),
00052     first_iframe_recv_(false),
00053     format_ctx_ptr_(NULL),
00054     codec_ctx_ptr_(NULL),
00055     codec_ptr_(NULL),
00056     frame_ptr_(NULL),
00057     frame_rgb_ptr_(NULL),
00058     img_convert_ctx_ptr_(NULL),
00059     input_format_ptr_(NULL),
00060     frame_rgb_raw_ptr_(NULL),
00061     update_codec_params_(false)
00062 {}
00063 
00064 bool VideoDecoder::InitCodec()
00065 {
00066   if (codec_initialized_)
00067   {
00068     return true;
00069   }
00070 
00071   try
00072   {
00073     // Very first init
00074     avcodec_register_all();
00075     av_register_all();
00076     av_log_set_level(AV_LOG_QUIET);
00077 
00078     codec_ptr_ = avcodec_find_decoder(AV_CODEC_ID_H264);
00079     ThrowOnCondition(codec_ptr_ == NULL, "Codec H264 not found!");
00080 
00081     codec_ctx_ptr_ = avcodec_alloc_context3(codec_ptr_);
00082     codec_ctx_ptr_->pix_fmt = AV_PIX_FMT_YUV420P;
00083     codec_ctx_ptr_->skip_frame = AVDISCARD_DEFAULT;
00084     codec_ctx_ptr_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK;
00085     codec_ctx_ptr_->skip_loop_filter = AVDISCARD_DEFAULT;
00086     codec_ctx_ptr_->workaround_bugs = AVMEDIA_TYPE_VIDEO;
00087     codec_ctx_ptr_->codec_id = AV_CODEC_ID_H264;
00088     codec_ctx_ptr_->skip_idct = AVDISCARD_DEFAULT;
00089     // At the beginning we have no idea about the frame size
00090     codec_ctx_ptr_->width = 0;
00091     codec_ctx_ptr_->height = 0;
00092 
00093     if (codec_ptr_->capabilities & CODEC_CAP_TRUNCATED)
00094     {
00095       codec_ctx_ptr_->flags |= CODEC_FLAG_TRUNCATED;
00096     }
00097     codec_ctx_ptr_->flags2 |= CODEC_FLAG2_CHUNKS;
00098 
00099     frame_ptr_ = av_frame_alloc();
00100     ThrowOnCondition(!frame_ptr_ , "Can not allocate memory for frames!");
00101 
00102     ThrowOnCondition(
00103           avcodec_open2(codec_ctx_ptr_, codec_ptr_, NULL) < 0,
00104           "Can not open the decoder!");
00105 
00106     av_init_packet(&packet_);
00107   }
00108   catch (const std::runtime_error& e)
00109   {
00110     ARSAL_PRINT(ARSAL_PRINT_ERROR, LOG_TAG, "%s", e.what());
00111     Reset();
00112     return false;
00113   }
00114 
00115   codec_initialized_ = true;
00116   first_iframe_recv_ = false;
00117   ARSAL_PRINT(ARSAL_PRINT_INFO, LOG_TAG, "H264 Codec is partially initialized!");
00118   return true;
00119 }
00120 
00121 bool VideoDecoder::ReallocateBuffers()
00122 {
00123   ARSAL_PRINT(ARSAL_PRINT_INFO, LOG_TAG, "Buffer reallocation request");
00124   if (!codec_initialized_)
00125   {
00126     return false;
00127   }
00128 
00129   try
00130   {
00131     ThrowOnCondition(codec_ctx_ptr_->width == 0 || codec_ctx_ptr_->width == 0,
00132                      std::string("Invalid frame size:") +
00133                      boost::lexical_cast<std::string>(codec_ctx_ptr_->width) +
00134                      " x " + boost::lexical_cast<std::string>(codec_ctx_ptr_->width));
00135 
00136     const uint32_t num_bytes = avpicture_get_size(PIX_FMT_RGB24, codec_ctx_ptr_->width, codec_ctx_ptr_->width);
00137     frame_rgb_ptr_ = av_frame_alloc();
00138 
00139     ThrowOnCondition(!frame_rgb_ptr_, "Can not allocate memory for frames!");
00140 
00141     frame_rgb_raw_ptr_ = reinterpret_cast<uint8_t*>(av_malloc(num_bytes * sizeof(uint8_t)));
00142     ThrowOnCondition(frame_rgb_raw_ptr_ == NULL,
00143                      std::string("Can not allocate memory for the buffer: ") +
00144                      boost::lexical_cast<std::string>(num_bytes));
00145     ThrowOnCondition(0 == avpicture_fill(
00146                        reinterpret_cast<AVPicture*>(frame_rgb_ptr_), frame_rgb_raw_ptr_, PIX_FMT_RGB24,
00147                        codec_ctx_ptr_->width, codec_ctx_ptr_->height),
00148                      "Failed to initialize the picture data structure.");
00149 
00150     img_convert_ctx_ptr_ = sws_getContext(codec_ctx_ptr_->width, codec_ctx_ptr_->height, codec_ctx_ptr_->pix_fmt,
00151                                           codec_ctx_ptr_->width, codec_ctx_ptr_->height, PIX_FMT_RGB24,
00152                                           SWS_FAST_BILINEAR, NULL, NULL, NULL);
00153   }
00154   catch (const std::runtime_error& e)
00155   {
00156     ARSAL_PRINT(ARSAL_PRINT_ERROR, LOG_TAG, "%s", e.what());
00157     Reset();  // reset() is intentional
00158     return false;
00159   }
00160 
00161   return true;
00162 }
00163 
00164 void VideoDecoder::CleanupBuffers()
00165 {
00166   if (frame_rgb_ptr_)
00167   {
00168     av_free(frame_rgb_ptr_);
00169   }
00170 
00171   if (frame_rgb_raw_ptr_)
00172   {
00173     av_free(frame_rgb_raw_ptr_);
00174   }
00175 
00176   if (img_convert_ctx_ptr_)
00177   {
00178     sws_freeContext(img_convert_ctx_ptr_);
00179   }
00180 
00181   ARSAL_PRINT(ARSAL_PRINT_INFO, LOG_TAG, "Buffer cleanup!");
00182 }
00183 
00184 void VideoDecoder::Reset()
00185 {
00186   if (codec_ctx_ptr_)
00187   {
00188     avcodec_close(codec_ctx_ptr_);
00189   }
00190 
00191   if (frame_ptr_)
00192   {
00193     av_free(frame_ptr_);
00194   }
00195 
00196   CleanupBuffers();
00197 
00198   codec_initialized_ = false;
00199   first_iframe_recv_ = false;
00200   ARSAL_PRINT(ARSAL_PRINT_INFO, LOG_TAG, "Reset!");
00201 }
00202 
00203 VideoDecoder::~VideoDecoder()
00204 {
00205   Reset();
00206   ARSAL_PRINT(ARSAL_PRINT_INFO, LOG_TAG, "Dstr!");
00207 }
00208 
00209 void VideoDecoder::ConvertFrameToRGB()
00210 {
00211   if (!codec_ctx_ptr_->width || !codec_ctx_ptr_->height) return;
00212   sws_scale(img_convert_ctx_ptr_, frame_ptr_->data, frame_ptr_->linesize, 0,
00213             codec_ctx_ptr_->height, frame_rgb_ptr_->data, frame_rgb_ptr_->linesize);
00214 }
00215 
00216 bool VideoDecoder::SetH264Params(uint8_t *sps_buffer_ptr, uint32_t sps_buffer_size,
00217                                  uint8_t *pps_buffer_ptr, uint32_t pps_buffer_size)
00218 {
00219   // This function is called in the same thread as Decode(), so no sync is necessary
00220   // TODO: Exact sizes + more error checkings
00221   update_codec_params_ = (sps_buffer_ptr && pps_buffer_ptr &&
00222                           sps_buffer_size && pps_buffer_size &&
00223                           (pps_buffer_size < 32) && (sps_buffer_size < 32));
00224 
00225   if (update_codec_params_)
00226   {
00227     codec_data_.resize(sps_buffer_size + pps_buffer_size);
00228     std::copy(sps_buffer_ptr, sps_buffer_ptr + sps_buffer_size, codec_data_.begin());
00229     std::copy(pps_buffer_ptr, pps_buffer_ptr + pps_buffer_size, codec_data_.begin() + sps_buffer_size);
00230   }
00231   else
00232   {
00233     // invalid data
00234     codec_data_.clear();
00235   }
00236 
00237   return update_codec_params_;
00238 }
00239 
00240 bool VideoDecoder::Decode(const ARCONTROLLER_Frame_t *bebop_frame_ptr_)
00241 {
00242   if (!codec_initialized_)
00243   {
00244     if (!InitCodec())
00245     {
00246       ARSAL_PRINT(ARSAL_PRINT_WARNING, LOG_TAG, "Codec initialization failed!");
00247       return false;
00248     }
00249   }
00250 
00251   /*
00252    * For VideoStream2, we trick avcodec whenever we receive a new SPS/PPS
00253    * info from the Bebop. SetH264Params() function will fill a buffer with SPS/PPS
00254    * data, then these are passed to avcodec_decode_video2() here, once for each SPS/PPS update.
00255    * Apparantly, avcodec_decode_video2() function picks up the changes and apply them to
00256    * upcoming video packets.
00257    *
00258    * More info on VS v2.0: http://developer.parrot.com/blog/2016/ARSDK-3-8-release/
00259    *
00260    * */
00261   if (update_codec_params_ && codec_data_.size())
00262   {
00263     ARSAL_PRINT(ARSAL_PRINT_INFO, LOG_TAG, "Updating H264 codec parameters (Buffer Size: %lu) ...", codec_data_.size());
00264     packet_.data = &codec_data_[0];
00265     packet_.size = codec_data_.size();
00266     int32_t frame_finished = 0;
00267     const int32_t len = avcodec_decode_video2(codec_ctx_ptr_, frame_ptr_, &frame_finished, &packet_);
00268     if (len >= 0 && len == packet_.size)
00269     {
00270       // success, skip this step until next codec update
00271       update_codec_params_ = false;
00272     }
00273     else
00274     {
00275       ARSAL_PRINT(ARSAL_PRINT_ERROR, LOG_TAG, "Unexpected error while updating H264 parameters.");
00276       return false;
00277     }
00278   }
00279 
00280   if (!bebop_frame_ptr_->data || !bebop_frame_ptr_->used)
00281   {
00282     ARSAL_PRINT(ARSAL_PRINT_ERROR, LOG_TAG, "Invalid frame data. Skipping.");
00283     return false;
00284   }
00285 
00286   packet_.data = bebop_frame_ptr_->data;
00287   packet_.size = bebop_frame_ptr_->used;
00288 
00289   const uint32_t width_prev = GetFrameWidth();
00290   const uint32_t height_prev = GetFrameHeight();
00291 
00292   int32_t frame_finished = 0;
00293   while (packet_.size > 0)
00294   {
00295     const int32_t len = avcodec_decode_video2(codec_ctx_ptr_, frame_ptr_, &frame_finished, &packet_);
00296     if (len >= 0)
00297     {
00298       if (frame_finished)
00299       {
00300         if ((GetFrameWidth() != width_prev) || (GetFrameHeight() != height_prev))
00301         {
00302           ARSAL_PRINT(ARSAL_PRINT_ERROR, LOG_TAG, "Frame size changed to %u x %u", GetFrameWidth(), GetFrameHeight());
00303           if (!ReallocateBuffers())
00304           {
00305             ARSAL_PRINT(ARSAL_PRINT_ERROR, LOG_TAG, "Buffer reallocation failed!");
00306           }
00307         }
00308         ConvertFrameToRGB();
00309       }
00310 
00311       if (packet_.data)
00312       {
00313         packet_.size -= len;
00314         packet_.data += len;
00315       }
00316     }
00317     else
00318     {
00319       return false;
00320     }
00321   }
00322   return true;
00323 }
00324 
00325 }  // namespace bebop_driver


bebop_driver
Author(s): Mani Monajjemi
autogenerated on Mon Aug 21 2017 02:36:39