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
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
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
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();
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
00220
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
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
00253
00254
00255
00256
00257
00258
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
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 }