bebop_video_decoder.cpp
Go to the documentation of this file.
1 
26 
27 #include <stdexcept>
28 #include <algorithm>
29 #include <string>
30 
31 #include <boost/lexical_cast.hpp>
32 
33 extern "C"
34 {
35  #include "libARSAL/ARSAL_Print.h"
36 }
37 
38 namespace bebop_driver
39 {
40 
41 const char* VideoDecoder::LOG_TAG = "Decoder";
42 
43 // TODO(mani-monaj): Move to util, inline
44 void VideoDecoder::ThrowOnCondition(const bool cond, const std::string &message)
45 {
46  if (!cond) return;
47  throw std::runtime_error(message);
48 }
49 
51  : codec_initialized_(false),
52  first_iframe_recv_(false),
53  format_ctx_ptr_(NULL),
54  codec_ctx_ptr_(NULL),
55  codec_ptr_(NULL),
56  frame_ptr_(NULL),
57  frame_rgb_ptr_(NULL),
59  input_format_ptr_(NULL),
60  frame_rgb_raw_ptr_(NULL),
62 {}
63 
65 {
67  {
68  return true;
69  }
70 
71  try
72  {
73  // Very first init
74  avcodec_register_all();
75  av_register_all();
76  av_log_set_level(AV_LOG_QUIET);
77 
78  codec_ptr_ = avcodec_find_decoder(AV_CODEC_ID_H264);
79  ThrowOnCondition(codec_ptr_ == NULL, "Codec H264 not found!");
80 
81  codec_ctx_ptr_ = avcodec_alloc_context3(codec_ptr_);
82  codec_ctx_ptr_->pix_fmt = AV_PIX_FMT_YUV420P;
83  codec_ctx_ptr_->skip_frame = AVDISCARD_DEFAULT;
84  codec_ctx_ptr_->error_concealment = FF_EC_GUESS_MVS | FF_EC_DEBLOCK;
85  codec_ctx_ptr_->skip_loop_filter = AVDISCARD_DEFAULT;
86  codec_ctx_ptr_->workaround_bugs = AVMEDIA_TYPE_VIDEO;
87  codec_ctx_ptr_->codec_id = AV_CODEC_ID_H264;
88  codec_ctx_ptr_->skip_idct = AVDISCARD_DEFAULT;
89  // At the beginning we have no idea about the frame size
90  codec_ctx_ptr_->width = 0;
91  codec_ctx_ptr_->height = 0;
92 
93  if (codec_ptr_->capabilities & CODEC_CAP_TRUNCATED)
94  {
95  codec_ctx_ptr_->flags |= CODEC_FLAG_TRUNCATED;
96  }
97  codec_ctx_ptr_->flags2 |= CODEC_FLAG2_CHUNKS;
98 
99  frame_ptr_ = av_frame_alloc();
100  ThrowOnCondition(!frame_ptr_ , "Can not allocate memory for frames!");
101 
103  avcodec_open2(codec_ctx_ptr_, codec_ptr_, NULL) < 0,
104  "Can not open the decoder!");
105 
106  av_init_packet(&packet_);
107  }
108  catch (const std::runtime_error& e)
109  {
110  ARSAL_PRINT(ARSAL_PRINT_ERROR, LOG_TAG, "%s", e.what());
111  Reset();
112  return false;
113  }
114 
115  codec_initialized_ = true;
116  first_iframe_recv_ = false;
117  ARSAL_PRINT(ARSAL_PRINT_INFO, LOG_TAG, "H264 Codec is partially initialized!");
118  return true;
119 }
120 
122 {
123  ARSAL_PRINT(ARSAL_PRINT_INFO, LOG_TAG, "Buffer reallocation request");
124  if (!codec_initialized_)
125  {
126  return false;
127  }
128 
129  try
130  {
131  ThrowOnCondition(codec_ctx_ptr_->width == 0 || codec_ctx_ptr_->width == 0,
132  std::string("Invalid frame size:") +
133  boost::lexical_cast<std::string>(codec_ctx_ptr_->width) +
134  " x " + boost::lexical_cast<std::string>(codec_ctx_ptr_->width));
135 
136  const uint32_t num_bytes = avpicture_get_size(AV_PIX_FMT_RGB24, codec_ctx_ptr_->width, codec_ctx_ptr_->width);
137  frame_rgb_ptr_ = av_frame_alloc();
138 
139  ThrowOnCondition(!frame_rgb_ptr_, "Can not allocate memory for frames!");
140 
141  frame_rgb_raw_ptr_ = reinterpret_cast<uint8_t*>(av_malloc(num_bytes * sizeof(uint8_t)));
143  std::string("Can not allocate memory for the buffer: ") +
144  boost::lexical_cast<std::string>(num_bytes));
145  ThrowOnCondition(0 == avpicture_fill(
146  reinterpret_cast<AVPicture*>(frame_rgb_ptr_), frame_rgb_raw_ptr_, AV_PIX_FMT_RGB24,
147  codec_ctx_ptr_->width, codec_ctx_ptr_->height),
148  "Failed to initialize the picture data structure.");
149 
150  img_convert_ctx_ptr_ = sws_getContext(codec_ctx_ptr_->width, codec_ctx_ptr_->height, codec_ctx_ptr_->pix_fmt,
151  codec_ctx_ptr_->width, codec_ctx_ptr_->height, AV_PIX_FMT_RGB24,
152  SWS_FAST_BILINEAR, NULL, NULL, NULL);
153  }
154  catch (const std::runtime_error& e)
155  {
156  ARSAL_PRINT(ARSAL_PRINT_ERROR, LOG_TAG, "%s", e.what());
157  Reset(); // reset() is intentional
158  return false;
159  }
160 
161  return true;
162 }
163 
165 {
166  if (frame_rgb_ptr_)
167  {
168  av_free(frame_rgb_ptr_);
169  }
170 
171  if (frame_rgb_raw_ptr_)
172  {
173  av_free(frame_rgb_raw_ptr_);
174  }
175 
177  {
178  sws_freeContext(img_convert_ctx_ptr_);
179  }
180 
181  ARSAL_PRINT(ARSAL_PRINT_INFO, LOG_TAG, "Buffer cleanup!");
182 }
183 
185 {
186  if (codec_ctx_ptr_)
187  {
188  avcodec_close(codec_ctx_ptr_);
189  }
190 
191  if (frame_ptr_)
192  {
193  av_free(frame_ptr_);
194  }
195 
196  CleanupBuffers();
197 
198  codec_initialized_ = false;
199  first_iframe_recv_ = false;
200  ARSAL_PRINT(ARSAL_PRINT_INFO, LOG_TAG, "Reset!");
201 }
202 
204 {
205  Reset();
206  ARSAL_PRINT(ARSAL_PRINT_INFO, LOG_TAG, "Dstr!");
207 }
208 
210 {
211  if (!codec_ctx_ptr_->width || !codec_ctx_ptr_->height) return;
212  sws_scale(img_convert_ctx_ptr_, frame_ptr_->data, frame_ptr_->linesize, 0,
213  codec_ctx_ptr_->height, frame_rgb_ptr_->data, frame_rgb_ptr_->linesize);
214 }
215 
216 bool VideoDecoder::SetH264Params(uint8_t *sps_buffer_ptr, uint32_t sps_buffer_size,
217  uint8_t *pps_buffer_ptr, uint32_t pps_buffer_size)
218 {
219  // This function is called in the same thread as Decode(), so no sync is necessary
220  // TODO: Exact sizes + more error checkings
221  update_codec_params_ = (sps_buffer_ptr && pps_buffer_ptr &&
222  sps_buffer_size && pps_buffer_size &&
223  (pps_buffer_size < 32) && (sps_buffer_size < 32));
224 
226  {
227  codec_data_.resize(sps_buffer_size + pps_buffer_size);
228  std::copy(sps_buffer_ptr, sps_buffer_ptr + sps_buffer_size, codec_data_.begin());
229  std::copy(pps_buffer_ptr, pps_buffer_ptr + pps_buffer_size, codec_data_.begin() + sps_buffer_size);
230  }
231  else
232  {
233  // invalid data
234  codec_data_.clear();
235  }
236 
237  return update_codec_params_;
238 }
239 
240 bool VideoDecoder::Decode(const ARCONTROLLER_Frame_t *bebop_frame_ptr_)
241 {
242  if (!codec_initialized_)
243  {
244  if (!InitCodec())
245  {
246  ARSAL_PRINT(ARSAL_PRINT_WARNING, LOG_TAG, "Codec initialization failed!");
247  return false;
248  }
249  }
250 
251  /*
252  * For VideoStream2, we trick avcodec whenever we receive a new SPS/PPS
253  * info from the Bebop. SetH264Params() function will fill a buffer with SPS/PPS
254  * data, then these are passed to avcodec_decode_video2() here, once for each SPS/PPS update.
255  * Apparantly, avcodec_decode_video2() function picks up the changes and apply them to
256  * upcoming video packets.
257  *
258  * More info on VS v2.0: http://developer.parrot.com/blog/2016/ARSDK-3-8-release/
259  *
260  * */
261  if (update_codec_params_ && codec_data_.size())
262  {
263  ARSAL_PRINT(ARSAL_PRINT_INFO, LOG_TAG, "Updating H264 codec parameters (Buffer Size: %lu) ...", codec_data_.size());
264  packet_.data = &codec_data_[0];
265  packet_.size = codec_data_.size();
266  int32_t frame_finished = 0;
267  const int32_t len = avcodec_decode_video2(codec_ctx_ptr_, frame_ptr_, &frame_finished, &packet_);
268  if (len >= 0 && len == packet_.size)
269  {
270  // success, skip this step until next codec update
271  update_codec_params_ = false;
272  }
273  else
274  {
275  ARSAL_PRINT(ARSAL_PRINT_ERROR, LOG_TAG, "Unexpected error while updating H264 parameters.");
276  return false;
277  }
278  }
279 
280  if (!bebop_frame_ptr_->data || !bebop_frame_ptr_->used)
281  {
282  ARSAL_PRINT(ARSAL_PRINT_ERROR, LOG_TAG, "Invalid frame data. Skipping.");
283  return false;
284  }
285 
286  packet_.data = bebop_frame_ptr_->data;
287  packet_.size = bebop_frame_ptr_->used;
288 
289  const uint32_t width_prev = GetFrameWidth();
290  const uint32_t height_prev = GetFrameHeight();
291 
292  int32_t frame_finished = 0;
293  while (packet_.size > 0)
294  {
295  const int32_t len = avcodec_decode_video2(codec_ctx_ptr_, frame_ptr_, &frame_finished, &packet_);
296  if (len >= 0)
297  {
298  if (frame_finished)
299  {
300  if ((GetFrameWidth() != width_prev) || (GetFrameHeight() != height_prev))
301  {
302  ARSAL_PRINT(ARSAL_PRINT_ERROR, LOG_TAG, "Frame size changed to %u x %u", GetFrameWidth(), GetFrameHeight());
303  if (!ReallocateBuffers())
304  {
305  ARSAL_PRINT(ARSAL_PRINT_ERROR, LOG_TAG, "Buffer reallocation failed!");
306  }
307  }
309  }
310 
311  if (packet_.data)
312  {
313  packet_.size -= len;
314  packet_.data += len;
315  }
316  }
317  else
318  {
319  return false;
320  }
321  }
322  return true;
323 }
324 
325 } // namespace bebop_driver
uint32_t GetFrameHeight() const
bool Decode(const ARCONTROLLER_Frame_t *bebop_frame_ptr_)
bool SetH264Params(uint8_t *sps_buffer_ptr, uint32_t sps_buffer_size, uint8_t *pps_buffer_ptr, uint32_t pps_buffer_size)
std::vector< uint8_t > codec_data_
static void ThrowOnCondition(const bool cond, const std::string &message)
AVFormatContext * format_ctx_ptr_


bebop_driver
Author(s): Mani Monajjemi
autogenerated on Mon Jun 10 2019 12:58:56