Blue Brain BioExplorer
encoder.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2019 EPFL/Blue Brain Project
3  * All rights reserved. Do not distribute without permission.
4  *
5  * This file is part of Blue Brain BioExplorer <https://github.com/BlueBrain/BioExplorer>
6  *
7  * This library is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU Lesser General Public License version 3.0 as published
9  * by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13  * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
14  * details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20 
21 #include "encoder.h"
22 
25 
26 int custom_io_write(void *opaque, uint8_t *buffer, int32_t buffer_size)
27 {
28  auto encoder = (core::Encoder *)opaque;
29  encoder->_dataFunc((const char *)buffer, buffer_size);
30  return buffer_size;
31 }
32 
33 namespace core
34 {
35 Encoder::Encoder(const int width_, const int height_, const int fps,
36  const int64_t kbps_, const DataFunc &dataFunc)
37  : _dataFunc(dataFunc)
38  , width(width_)
39  , height(height_)
40  , kbps(kbps_)
41  , _fps(fps)
42 {
43 #ifndef FF_API_NEXT
44  av_register_all();
45 #endif
46  formatContext = avformat_alloc_context();
47  formatContext->oformat = av_guess_format("mp4", nullptr, nullptr);
48  formatContext->flags = AVFMT_FLAG_CUSTOM_IO;
49 
50  const AVCodecID codecID = AV_CODEC_ID_H264;
51  codec = avcodec_find_encoder(codecID);
52  if (!codec)
53  CORE_THROW(
54  std::runtime_error(std::string("Could not find encoder for ") +
55  avcodec_get_name(codecID)));
56 
57  if (!(stream = avformat_new_stream(formatContext, codec)))
58  CORE_THROW(std::runtime_error("Could not create stream"));
59 
60  const AVRational avFPS = {fps, 1};
61  stream->avg_frame_rate = avFPS;
62  stream->time_base = av_inv_q(avFPS);
63 
64 #pragma GCC diagnostic push
65 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
66  codecContext = stream->codec;
67 #pragma GCC diagnostic pop
68  codecContext->codec_tag = 0;
69  codecContext->codec_id = codecID;
70  codecContext->codec_type = AVMEDIA_TYPE_VIDEO;
71  codecContext->width = width;
72  codecContext->height = height;
73  codecContext->gop_size = 0;
74  codecContext->pix_fmt = AV_PIX_FMT_YUV420P;
75  codecContext->framerate = avFPS;
76  codecContext->time_base = av_inv_q(avFPS);
77  codecContext->bit_rate = kbps * 1000;
78  codecContext->max_b_frames = 0;
79  codecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
80 
81  codecContext->profile = 100;
82  codecContext->level = 31;
83 
84  av_opt_set(codecContext->priv_data, "crf", "12", 0);
85  av_opt_set(codecContext->priv_data, "preset", "ultrafast", 0);
86  // av_opt_set(codecContext->priv_data, "profile", "main", 0);
87  av_opt_set(codecContext->priv_data, "tune", "zerolatency", 0);
88 
89  if (avcodec_open2(codecContext, codec, NULL) < 0)
90  CORE_THROW(std::runtime_error("Could not open video encoder!"));
91 
92  int avio_buffer_size = 1 * 1024 * 1024;
93  void *avio_buffer = av_malloc(avio_buffer_size);
94 
95  AVIOContext *custom_io =
96  avio_alloc_context((unsigned char *)avio_buffer, avio_buffer_size, 1,
97  (void *)this, NULL, &custom_io_write, NULL);
98 
99  formatContext->pb = custom_io;
100 
101  AVDictionary *fmt_opts = NULL;
102  av_dict_set(&fmt_opts, "brand", "mp42", 0);
103  av_dict_set(&fmt_opts, "movflags", "faststart+frag_keyframe+empty_moov", 0);
104  av_dict_set(&fmt_opts, "live", "1", 0);
105  if (avformat_write_header(formatContext, &fmt_opts) < 0)
106  CORE_THROW(std::runtime_error("Could not write header!"));
107 
108  picture.init(codecContext->pix_fmt, width, height);
109 
110  if (_async)
111  _thread = std::thread(std::bind(&Encoder::_runAsync, this));
112 
113  _timer.start();
114 }
115 
117 {
118  if (_async)
119  {
120  _running = false;
121  _queue.clear();
122  _queue.push(-1);
123  _thread.join();
124  }
125 
126  if (formatContext)
127  {
128  av_write_trailer(formatContext);
129  av_free(formatContext->pb);
130  avcodec_close(codecContext);
131  avformat_free_context(formatContext);
132  }
133 }
134 
136 {
137  if (_async && _queue.size() == 2)
138  return;
139 
140  fb.map();
141  auto cdata = reinterpret_cast<const uint8_t *const>(fb.getColorBuffer());
142  if (_async)
143  {
144  auto &image = _image[_currentImage];
145  image.width = fb.getSize().x;
146  image.height = fb.getSize().y;
147  const auto bufferSize = image.width * image.height * fb.getColorDepth();
148 
149  if (image.data.size() < bufferSize)
150  image.data.resize(bufferSize);
151  memcpy(image.data.data(), cdata, bufferSize);
152  _queue.push(_currentImage);
153  _currentImage = _currentImage == 0 ? 1 : 0;
154  fb.unmap();
155  return;
156  }
157 
158  _toPicture(cdata, fb.getSize().x, fb.getSize().y);
159  fb.unmap();
160 
161  _encode();
162 }
163 
164 void Encoder::_encode()
165 {
166  const auto elapsed = _timer.elapsed() + _leftover;
167  const auto duration = 1.0 / _fps;
168  if (elapsed < duration)
169  return;
170 
171  _leftover = elapsed - duration;
172  for (; _leftover > duration;)
173  _leftover -= duration;
174 
175  picture.frame->pts = _frameNumber++;
176 
177  if (avcodec_send_frame(codecContext, picture.frame) < 0)
178  return;
179 
180  AVPacket pkt;
181  av_init_packet(&pkt);
182  const auto ret = avcodec_receive_packet(codecContext, &pkt);
183  if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
184  {
185  av_packet_unref(&pkt);
186  return;
187  }
188 
189  av_packet_rescale_ts(&pkt, codecContext->time_base, stream->time_base);
190  pkt.stream_index = stream->index;
191  av_interleaved_write_frame(formatContext, &pkt);
192 
193  _timer.start();
194 }
195 
196 void Encoder::_runAsync()
197 {
198  while (_running)
199  {
200  auto idx = _queue.pop();
201  if (idx < 0)
202  break;
203 
204  auto &image = _image[idx];
205 
206  _toPicture(image.data.data(), image.width, image.height);
207  _encode();
208  }
209 }
210 
211 void Encoder::_toPicture(const uint8_t *const data, const int width_,
212  const int height_)
213 {
214  sws_context =
215  sws_getCachedContext(sws_context, width_, height_, AV_PIX_FMT_RGBA,
216  width, height, AV_PIX_FMT_YUV420P,
217  SWS_FAST_BILINEAR, 0, 0, 0);
218  const int stride[] = {4 * (int)width_};
219  sws_scale(sws_context, &data, stride, 0, height_, picture.frame->data,
220  picture.frame->linesize);
221 }
222 } // namespace core
std::function< void(const char *data, size_t size)> DataFunc
Definition: encoder.h:114
void encode(FrameBuffer &fb)
Definition: encoder.cpp:135
const int width
Definition: encoder.h:123
Encoder(const int width, const int height, const int fps, const int64_t kbps, const DataFunc &dataFunc)
Definition: encoder.cpp:35
const int64_t kbps
Definition: encoder.h:125
const int height
Definition: encoder.h:124
This class represents a frame buffer for an engine specific code. It provides an API for utilizing an...
Definition: FrameBuffer.h:39
virtual PLATFORM_API const uint8_t * getColorBuffer() const =0
Get the Color Buffer object.
virtual PLATFORM_API void map()=0
Map the buffer for reading with get*Buffer().
PLATFORM_API size_t getColorDepth() const
Get the Color Depth object.
Definition: FrameBuffer.cpp:34
virtual PLATFORM_API Vector2ui getSize() const
Get the Size object.
Definition: FrameBuffer.h:97
virtual PLATFORM_API void unmap()=0
Unmap the buffer for reading with get*Buffer().
void clear()
Definition: encoder.h:73
size_t size() const
Definition: encoder.h:98
void push(const T &element)
Definition: encoder.h:80
AVFrame * frame
Definition: encoder.h:46
int init(enum AVPixelFormat pix_fmt, int width, int height)
Definition: encoder.h:48
void start()
Definition: Timer.cpp:34
double elapsed() const
Definition: Timer.cpp:39
int custom_io_write(void *opaque, uint8_t *buffer, int32_t buffer_size)
Definition: encoder.cpp:26
#define CORE_THROW(__msg)
Definition: Logs.h:42