h264_encoder.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  */
19 
20 #include <aws/core/utils/logging/LogMacros.h>
22 
23 #include <cstdio>
24 #include <dlfcn.h>
25 
26 extern "C" {
27 #include <libavcodec/avcodec.h>
28 #include <libavutil/imgutils.h>
29 #include <libavutil/opt.h>
30 #include <libswscale/swscale.h>
31 }
32 
33 using namespace Aws::Client;
34 using namespace Aws::Utils::Logging;
35 
36 
37 
38 namespace Aws {
39 namespace Utils {
40 namespace Encoding {
41 
42 constexpr char kOutputWidthKey[] = "output_width";
43 constexpr char kOutputHeightKey[] = "output_height";
44 constexpr char kFpsNumeratorKey[] = "fps_numerator";
45 constexpr char kFpsDenominatorKey[] = "fps_denominator";
46 constexpr char kCodecKey[] = "codec";
47 constexpr char kBitrateKey[] = "bitrate";
48 
49 constexpr char kDefaultHardwareCodec[] = "h264_omx";
50 constexpr char kDefaultSoftwareCodec[] = "libx264";
51 constexpr float kFragmentDuration = 1.0f; /* set fragment duration to 1.0 second */
52 constexpr int kDefaultMaxBFrames = 0;
53 constexpr int kDefaultFpsNumerator = 24;
54 constexpr int kDefaultFpsDenominator = 1;
55 constexpr int kDefaultBitrate = 2048000;
56 
58 {
59 public:
61  : src_width_(-1),
62  src_height_(-1),
63  src_encoding_(AV_PIX_FMT_NONE),
64  src_stride_(-1),
65  dst_width_(-1),
66  dst_height_(-1),
67  convert_ctx_(nullptr),
68  bitrate_(-1),
69  fps_num_(-1),
70  fps_den_(-1),
71  param_(nullptr),
72  pic_in_(nullptr)
73  {
74  }
75 
76  /* Setup param_
77  * Function will fail if param_ is not nullptr
78  */
79  AwsError set_param(AVCodec * codec) {
80  if (nullptr != param_) {
81  AWS_LOG_ERROR(__func__, "Unable to setup codec context. param_ must be null");
82  return AWS_ERR_FAILURE;
83  }
84  param_ = avcodec_alloc_context3(codec);
85  if (nullptr == param_) {
86  AWS_LOG_ERROR(__func__, "Could not allocate video codec context");
87  return AWS_ERR_MEMORY;
88  }
89  /* put sample parameters */
90  param_->bit_rate = bitrate_;
91  /* resolution must be a multiple of two */
92  param_->width = dst_width_;
93  param_->height = dst_height_;
94  /* frames per second */
95  param_->time_base = (AVRational){fps_den_, fps_num_};
96  frame_duration_ = (1e6 * fps_den_) / fps_num_;
97  param_->gop_size = static_cast<int>(ceil(kFragmentDuration * fps_num_ / fps_den_));
98  param_->keyint_min = param_->gop_size - 1;
99  param_->max_b_frames = kDefaultMaxBFrames;
100  param_->pix_fmt = AV_PIX_FMT_YUV420P;
101 
102  param_->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
103  param_->flags2 &= ~AV_CODEC_FLAG2_LOCAL_HEADER;
104 
105  return AWS_ERR_OK;
106  }
107 
108  // Attempts to open a codec
109  AwsError open_codec(AVCodec * codec, AVDictionary * opts) {
110  if (nullptr == codec) {
111  AWS_LOG_ERROR(__func__, "Invalid codec");
112  return AWS_ERR_FAILURE;
113  }
114 
115  AWS_LOGSTREAM_INFO(__func__, "Attempting to open codec: " << codec->name);
116 
117  if (AWS_ERR_OK != set_param(codec) || avcodec_open2(param_, codec, &opts) < 0 ) {
118  AWS_LOG_ERROR(__func__, "Could not open codec");
119  if (nullptr != param_) {
120  avcodec_close(param_);
121  av_free(param_);
122  param_ = nullptr;
123  }
124  return AWS_ERR_FAILURE;
125  }
126 
127  return AWS_ERR_OK;
128  }
129 
130  AwsError Initialize(const int src_width, const int src_height, const AVPixelFormat src_encoding,
131  const std::string & codec_name, const int dst_width, const int dst_height,
132  const int fps_num, const int fps_den, const int64_t bitrate)
133  {
134  if (src_width <= 0) {
135  AWS_LOGSTREAM_ERROR(__func__, "Invalid video source width " << src_width << "!");
136  return AWS_ERR_PARAM;
137  }
138  if (src_height <= 0) {
139  AWS_LOGSTREAM_ERROR(__func__, "Invalid video source height " << src_height << "!");
140  return AWS_ERR_PARAM;
141  }
142  if (dst_width <= 0) {
143  AWS_LOGSTREAM_ERROR(__func__, "Invalid output video width " << dst_width << "!");
144  return AWS_ERR_PARAM;
145  }
146  if (dst_height <= 0) {
147  AWS_LOGSTREAM_ERROR(__func__, "Invalid video source height " << dst_height << "!");
148  return AWS_ERR_PARAM;
149  }
150  if (fps_num <= 0) {
151  AWS_LOGSTREAM_ERROR(__func__, "Invalid FPS numerator " << fps_num << "!");
152  return AWS_ERR_PARAM;
153  }
154  if (fps_den <= 0) {
155  AWS_LOGSTREAM_ERROR(__func__, "Invalid FPS denominator " << fps_den << "!");
156  return AWS_ERR_PARAM;
157  }
158  if (bitrate <= 0) {
159  AWS_LOGSTREAM_ERROR(__func__, "Invalid bit rate " << bitrate << "!");
160  return AWS_ERR_PARAM;
161  }
162 
163  src_width_ = src_width;
164  src_height_ = src_height;
165  src_encoding_ = src_encoding;
166  if (src_encoding_ == AV_PIX_FMT_RGB24) {
167  src_stride_ = 3 * src_width_; // 3 color channels (red, green, blue)
168  } else if (src_encoding_ == AV_PIX_FMT_BGR24) {
169  src_stride_ = 3 * src_width_; // 3 color channels (blue, green, red)
170  } else if (src_encoding_ == AV_PIX_FMT_RGBA) {
171  src_stride_ = 4 * src_width_; // 4 color channels (red, green, blue, alpha)
172  } else if (src_encoding_ == AV_PIX_FMT_BGRA) {
173  src_stride_ = 4 * src_width_; // 4 color channels (blue, green, red, alpha)
174  } else {
175  AWS_LOG_ERROR(__func__, "Trying to work with unsupported encoding!");
176  return AWS_ERR_PARAM;
177  }
178 
179  dst_width_ = dst_width;
180  dst_height_ = dst_height;
181  fps_num_ = fps_num;
182  fps_den_ = fps_den;
183  bitrate_ = bitrate;
184 
185  avcodec_register_all();
186 
187  /* find the mpeg1 video encoder */
188  AVCodec * codec = nullptr;
189  AVDictionary * opts = nullptr;
190  if (codec_name.empty()) {
191  codec = avcodec_find_encoder_by_name(kDefaultHardwareCodec);
192  if (AWS_ERR_OK != open_codec(codec, opts)) {
193  codec = avcodec_find_encoder_by_name(kDefaultSoftwareCodec);
194  av_dict_set(&opts, "preset", "veryfast", 0);
195  av_dict_set(&opts, "tune", "zerolatency", 0);
196 
197  if (AWS_ERR_OK != open_codec(codec, opts)) {
198  AWS_LOGSTREAM_ERROR(__func__, kDefaultHardwareCodec << " and " << kDefaultSoftwareCodec
199  << " codecs were not available!");
200  return AWS_ERR_NOT_FOUND;
201  }
202  }
203  } else {
204  codec = avcodec_find_encoder_by_name(codec_name.c_str());
205  if (AWS_ERR_OK != open_codec(codec, opts)) {
206  AWS_LOGSTREAM_ERROR(__func__, codec_name << " codec not found!");
207  return AWS_ERR_NOT_FOUND;
208  }
209  }
210  AWS_LOGSTREAM_INFO(__func__, "Encoding using " << codec->name << " codec");
211 
212 
213 
214  dst_width_ = param_->width;
215  dst_height_ = param_->height;
216 
217  pic_in_ = av_frame_alloc();
218  if (nullptr == pic_in_) {
219  AWS_LOG_ERROR(__func__, "Could not allocate video frame");
220  return AWS_ERR_MEMORY;
221  }
222  pic_in_->format = param_->pix_fmt;
223  pic_in_->width = param_->width;
224  pic_in_->height = param_->height;
225  pic_in_->pts = 0;
226 
227  /* the image can be allocated by any means and av_image_alloc() is
228  * just the most convenient way if av_malloc() is to be used */
229  int ret = av_image_alloc(pic_in_->data, pic_in_->linesize, param_->width, param_->height,
230  param_->pix_fmt, 32);
231  if (ret < 0) {
232  AWS_LOGSTREAM_ERROR(__func__,
233  "Could not allocate raw picture buffer"
234  " (av_image_alloc() returned: "
235  << ret << ")");
236  return AWS_ERR_MEMORY;
237  }
238 
239  convert_ctx_ = sws_getContext(src_width_, src_height_, src_encoding_, dst_width_, dst_height_,
240  AV_PIX_FMT_YUV420P, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
241 
242  return AWS_ERR_OK;
243  }
244 
246  {
247  if (nullptr != convert_ctx_) {
248  sws_freeContext(convert_ctx_);
249  }
250 
251  if (nullptr != param_) {
252  avcodec_close(param_);
253  av_free(param_);
254  }
255 
256  if (nullptr != pic_in_) {
257  av_freep(&pic_in_->data[0]);
258  av_frame_free(&pic_in_);
259  }
260  }
261 
262  AwsError Encode(const uint8_t * img_data, H264EncoderResult & res)
263  {
264  if (nullptr == img_data) {
265  return AWS_ERR_NULL_PARAM;
266  }
267 
268  AVPacket pkt;
269 
270  /* Convert from image encoding to YUV420P */
271  const uint8_t * buf_in[4] = {img_data, nullptr, nullptr, nullptr};
272  sws_scale(convert_ctx_, (const uint8_t * const *)buf_in, &src_stride_, 0, src_height_,
273  pic_in_->data, pic_in_->linesize);
274 
275  /* Encode */
276  av_init_packet(&pkt);
277  pkt.data = nullptr; // packet data will be allocated by the encoder
278  pkt.size = 0;
279 
280  int got_output = 0;
281 
282  int ret = avcodec_encode_video2(param_, &pkt, pic_in_, &got_output);
283  ++pic_in_->pts;
284  if (ret < 0) {
285  AWS_LOGSTREAM_ERROR(__func__,
286  "Error encoding frame (avcodec_encode_video2() returned: " << ret << ")");
287  return AWS_ERR_FAILURE;
288  }
289  if (got_output) {
290  res.Reset();
291  res.frame_data.insert(res.frame_data.end(), pkt.data, pkt.data + pkt.size);
292  res.frame_pts = frame_duration_ * (0 <= pkt.pts ? pkt.pts : 0);
293  res.frame_dts = frame_duration_ * (0 <= pkt.dts ? pkt.dts : 0);
294  res.frame_duration = frame_duration_;
295  res.key_frame = pkt.flags & AV_PKT_FLAG_KEY;
296  av_free_packet(&pkt);
297 
298  return AWS_ERR_OK;
299  }
300 
301  return AWS_ERR_EMPTY;
302  }
303 
304  std::vector<uint8_t> GetExtraData() const
305  {
306  if (param_ != nullptr && param_->extradata != nullptr && param_->extradata_size > 0) {
307  return std::vector<uint8_t>(param_->extradata, param_->extradata + param_->extradata_size);
308  } else {
309  return std::vector<uint8_t>();
310  }
311  }
312 
313 private:
316  AVPixelFormat src_encoding_;
320  int bitrate_;
321  struct SwsContext * convert_ctx_;
322 
323  int fps_num_;
324  int fps_den_;
325  uint64_t frame_duration_; // in microseconds
326  AVCodecContext * param_;
327  AVFrame * pic_in_;
328 };
329 
330 H264Encoder::H264Encoder() {}
331 
332 H264Encoder::~H264Encoder() {}
333 
334 AwsError H264Encoder::Initialize(const int src_width, const int src_height,
335  const AVPixelFormat src_encoding,
336  const ParameterReaderInterface & dst_params)
337 {
338  int dst_width, dst_height;
339  bool dims_set = (dst_params.ReadParam(ParameterPath(kOutputWidthKey), dst_width) == Aws::AWS_ERR_OK &&
340  dst_params.ReadParam(ParameterPath(kOutputHeightKey), dst_height) == Aws::AWS_ERR_OK);
341  if (!dims_set) {
342  dst_width = src_width;
343  dst_height = src_height;
344  }
345 
346  int fps_num, fps_den;
347  bool fps_set = (dst_params.ReadParam(ParameterPath(kFpsNumeratorKey), fps_num) == Aws::AWS_ERR_OK &&
348  dst_params.ReadParam(ParameterPath(kFpsDenominatorKey), fps_den) == Aws::AWS_ERR_OK);
349  if (!fps_set) {
350  AWS_LOG_WARN(__func__, "fps not set");
351  fps_num = kDefaultFpsNumerator;
352  fps_den = kDefaultFpsDenominator;
353  }
354 
355  std::string codec;
356  dst_params.ReadParam(ParameterPath(kCodecKey), codec);
357 
358  int bitrate = kDefaultBitrate;
359  dst_params.ReadParam(ParameterPath(kBitrateKey), bitrate);
360 
361  impl_ = std::unique_ptr<H264EncoderImpl>(new H264EncoderImpl());
362 
363  return impl_->Initialize(src_width, src_height, src_encoding, codec, dst_width, dst_height,
364  fps_num, fps_den, bitrate);
365 }
366 
367 AwsError H264Encoder::Encode(const uint8_t * img_data, H264EncoderResult & res) const
368 {
369  return impl_->Encode(img_data, res);
370 }
371 
372 std::vector<uint8_t> H264Encoder::GetExtraData() const { return impl_->GetExtraData(); }
373 
374 } // namespace Encoding
375 } // namespace Utils
376 } // namespace Aws
constexpr char kOutputWidthKey[]
constexpr int kDefaultFpsNumerator
std::vector< uint8_t > GetExtraData() const
constexpr char kDefaultHardwareCodec[]
constexpr int kDefaultMaxBFrames
AwsError set_param(AVCodec *codec)
constexpr char kDefaultSoftwareCodec[]
AWS_ERR_PARAM
constexpr float kFragmentDuration
AwsError open_codec(AVCodec *codec, AVDictionary *opts)
AwsError Encode(const uint8_t *img_data, H264EncoderResult &res)
constexpr char kFpsDenominatorKey[]
constexpr int kDefaultFpsDenominator
AwsError Initialize(const int src_width, const int src_height, const AVPixelFormat src_encoding, const std::string &codec_name, const int dst_width, const int dst_height, const int fps_num, const int fps_den, const int64_t bitrate)
AWS_ERR_NULL_PARAM
virtual AwsError ReadParam(const ParameterPath &param_path, std::vector< std::string > &out) const =0
AWS_ERR_EMPTY
constexpr int kDefaultBitrate
AWS_ERR_NOT_FOUND
AWS_ERR_MEMORY
constexpr char kBitrateKey[]
AWS_ERR_OK
AWS_ERR_FAILURE
constexpr char kCodecKey[]
constexpr char kOutputHeightKey[]
constexpr char kFpsNumeratorKey[]


h264_encoder_core
Author(s): AWS RoboMaker
autogenerated on Fri Mar 5 2021 03:31:35