29 encoder->_dataFunc((
const char *)buffer, buffer_size);
36 const int64_t kbps_,
const DataFunc &dataFunc)
46 formatContext = avformat_alloc_context();
47 formatContext->oformat = av_guess_format(
"mp4",
nullptr,
nullptr);
48 formatContext->flags = AVFMT_FLAG_CUSTOM_IO;
50 const AVCodecID codecID = AV_CODEC_ID_H264;
51 codec = avcodec_find_encoder(codecID);
54 std::runtime_error(std::string(
"Could not find encoder for ") +
55 avcodec_get_name(codecID)));
57 if (!(stream = avformat_new_stream(formatContext, codec)))
58 CORE_THROW(std::runtime_error(
"Could not create stream"));
60 const AVRational avFPS = {fps, 1};
61 stream->avg_frame_rate = avFPS;
62 stream->time_base = av_inv_q(avFPS);
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;
81 codecContext->profile = 100;
82 codecContext->level = 31;
84 av_opt_set(codecContext->priv_data,
"crf",
"12", 0);
85 av_opt_set(codecContext->priv_data,
"preset",
"ultrafast", 0);
87 av_opt_set(codecContext->priv_data,
"tune",
"zerolatency", 0);
89 if (avcodec_open2(codecContext, codec, NULL) < 0)
90 CORE_THROW(std::runtime_error(
"Could not open video encoder!"));
92 int avio_buffer_size = 1 * 1024 * 1024;
93 void *avio_buffer = av_malloc(avio_buffer_size);
95 AVIOContext *custom_io =
96 avio_alloc_context((
unsigned char *)avio_buffer, avio_buffer_size, 1,
99 formatContext->pb = custom_io;
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!"));
111 _thread = std::thread(std::bind(&Encoder::_runAsync,
this));
128 av_write_trailer(formatContext);
129 av_free(formatContext->pb);
130 avcodec_close(codecContext);
131 avformat_free_context(formatContext);
137 if (_async && _queue.
size() == 2)
141 auto cdata =
reinterpret_cast<const uint8_t *const
>(fb.
getColorBuffer());
144 auto &image = _image[_currentImage];
147 const auto bufferSize = image.width * image.height * fb.
getColorDepth();
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;
164 void Encoder::_encode()
166 const auto elapsed = _timer.
elapsed() + _leftover;
167 const auto duration = 1.0 / _fps;
168 if (elapsed < duration)
171 _leftover = elapsed - duration;
172 for (; _leftover > duration;)
173 _leftover -= duration;
175 picture.
frame->pts = _frameNumber++;
177 if (avcodec_send_frame(codecContext, picture.
frame) < 0)
181 av_init_packet(&pkt);
182 const auto ret = avcodec_receive_packet(codecContext, &pkt);
183 if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
185 av_packet_unref(&pkt);
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);
196 void Encoder::_runAsync()
200 auto idx = _queue.
pop();
204 auto &image = _image[idx];
206 _toPicture(image.data.data(), image.width, image.height);
211 void Encoder::_toPicture(
const uint8_t *
const data,
const int width_,
215 sws_getCachedContext(sws_context, width_, height_, AV_PIX_FMT_RGBA,
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);
std::function< void(const char *data, size_t size)> DataFunc
void encode(FrameBuffer &fb)
Encoder(const int width, const int height, const int fps, const int64_t kbps, const DataFunc &dataFunc)
This class represents a frame buffer for an engine specific code. It provides an API for utilizing an...
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.
virtual PLATFORM_API Vector2ui getSize() const
Get the Size object.
virtual PLATFORM_API void unmap()=0
Unmap the buffer for reading with get*Buffer().
void push(const T &element)
int init(enum AVPixelFormat pix_fmt, int width, int height)
int custom_io_write(void *opaque, uint8_t *buffer, int32_t buffer_size)