Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/utils/gui/div/GUIVideoEncoder.h
169684 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2001-2025 German Aerospace Center (DLR) and others.
4
// This program and the accompanying materials are made available under the
5
// terms of the Eclipse Public License 2.0 which is available at
6
// https://www.eclipse.org/legal/epl-2.0/
7
// This Source Code may also be made available under the following Secondary
8
// Licenses when the conditions for such availability set forth in the Eclipse
9
// Public License 2.0 are satisfied: GNU General Public License, version 2
10
// or later which is available at
11
// https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13
/****************************************************************************/
14
/// @file GUIVideoEncoder.h
15
/// @author Michael Behrisch
16
/// @date Dec 2015
17
///
18
// A simple video encoder from RGBA pics to anything ffmpeg can handle.
19
// Tested with h264 only.
20
// Inspired by Lei Xiaohua, Philip Schneider and Fabrice Bellard, see
21
// https://github.com/leixiaohua1020/simplest_ffmpeg_video_encoder and
22
// https://github.com/codefromabove/FFmpegRGBAToYUV
23
/****************************************************************************/
24
#pragma once
25
#include <config.h>
26
27
#include <stdio.h>
28
#include <iostream>
29
#include <stdexcept>
30
31
#define __STDC_CONSTANT_MACROS
32
33
#ifdef _MSC_VER
34
#pragma warning(push)
35
#pragma warning(disable: 4242 4244) // do not warn about integer conversions
36
#endif
37
#if __GNUC__ > 3
38
#pragma GCC diagnostic push
39
#pragma GCC diagnostic ignored "-Wpedantic"
40
#pragma GCC diagnostic ignored "-Wvariadic-macros"
41
#endif
42
extern "C"
43
{
44
#include <libavutil/opt.h>
45
#include <libavutil/imgutils.h>
46
#include <libavcodec/avcodec.h>
47
#include <libavformat/avformat.h>
48
#include <libswscale/swscale.h>
49
}
50
#ifdef _MSC_VER
51
#pragma warning(pop)
52
#endif
53
#if __GNUC__ > 3
54
#pragma GCC diagnostic pop
55
#endif
56
57
#include <utils/common/MsgHandler.h>
58
#include <utils/common/ToString.h>
59
60
61
// ===========================================================================
62
// class definitions
63
// ===========================================================================
64
/**
65
* @class GUIVideoEncoder
66
* @brief A simple video encoder from RGBA pics to anything ffmpeg can handle.
67
*/
68
class GUIVideoEncoder {
69
public:
70
GUIVideoEncoder(const char* const out_file, const int width, const int height, double frameDelay) {
71
//av_register_all();
72
avformat_alloc_output_context2(&myFormatContext, NULL, NULL, out_file);
73
if (myFormatContext == nullptr) {
74
throw ProcessError(TL("Unknown format!"));
75
}
76
77
// @todo maybe warn about default and invalid framerates
78
int framerate = 25;
79
if (frameDelay > 0.) {
80
framerate = (int)(1000. / frameDelay);
81
if (framerate <= 0) {
82
framerate = 1;
83
}
84
}
85
AVStream* const video_st = avformat_new_stream(myFormatContext, 0);
86
video_st->time_base.num = 1;
87
video_st->time_base.den = framerate;
88
89
const AVCodec* codec = avcodec_find_encoder(myFormatContext->oformat->video_codec);
90
if (codec == nullptr) {
91
WRITE_WARNING(TL("Unknown codec, falling back to HEVC!"));
92
codec = avcodec_find_encoder_by_name("libx265");
93
}
94
if (codec == nullptr) {
95
throw ProcessError(TL("Unknown codec!"));
96
}
97
//Param that must set
98
myCodecCtx = avcodec_alloc_context3(codec);
99
if (myCodecCtx == nullptr) {
100
throw ProcessError(TL("Could not allocate video codec context!"));
101
}
102
//pmyCodecCtx->codec_id =AV_CODEC_ID_HEVC;
103
//pmyCodecCtx->codec_id = pFormatCtx->oformat->video_codec;
104
//pmyCodecCtx->codec_type = AVMEDIA_TYPE_VIDEO;
105
myCodecCtx->pix_fmt = AV_PIX_FMT_YUV420P;
106
// @todo maybe warn about one missing line for odd width or height
107
myCodecCtx->width = (width / 2) * 2;
108
myCodecCtx->height = (height / 2) * 2;
109
myCodecCtx->time_base.num = 1;
110
myCodecCtx->time_base.den = framerate;
111
myCodecCtx->framerate.num = framerate;
112
myCodecCtx->framerate.den = 1;
113
myCodecCtx->bit_rate = 4000000; // example has 400000
114
myCodecCtx->gop_size = 10; // example has 10
115
//H264
116
//pmyCodecCtx->me_range = 16;
117
//pmyCodecCtx->max_qdiff = 4;
118
//pmyCodecCtx->qcompress = 0.6;
119
//myCodecCtx->qmin = 10; // example does not set this
120
//myCodecCtx->qmax = 51; // example does not set this
121
//myCodecCtx->max_b_frames = 1; // example has 1
122
123
// Set codec specific options
124
//H.264
125
if (myCodecCtx->codec_id == AV_CODEC_ID_H264) {
126
av_opt_set(myCodecCtx->priv_data, "preset", "slow", 0);
127
//av_opt_set(myCodecCtx->priv_data, "tune", "zerolatency", 0);
128
//av_opt_set(myCodecCtx->priv_data, "profile", "main", 0);
129
}
130
//H.265
131
if (myCodecCtx->codec_id == AV_CODEC_ID_HEVC) {
132
av_opt_set(myCodecCtx->priv_data, "preset", "ultrafast", 0);
133
av_opt_set(myCodecCtx->priv_data, "tune", "zero-latency", 0);
134
}
135
if (avcodec_open2(myCodecCtx, codec, nullptr) < 0) {
136
throw ProcessError(TL("Could not open codec!"));
137
}
138
avcodec_parameters_from_context(video_st->codecpar, myCodecCtx);
139
140
myFrame = av_frame_alloc();
141
if (myFrame == nullptr) {
142
throw ProcessError(TL("Could not allocate video frame!"));
143
}
144
myFrame->format = myCodecCtx->pix_fmt;
145
myFrame->width = myCodecCtx->width;
146
myFrame->height = myCodecCtx->height;
147
if (av_frame_get_buffer(myFrame, 32) < 0) {
148
throw ProcessError(TL("Could not allocate the video frame data!"));
149
}
150
mySwsContext = sws_getContext(myCodecCtx->width, myCodecCtx->height, AV_PIX_FMT_RGBA,
151
myCodecCtx->width, myCodecCtx->height, AV_PIX_FMT_YUV420P,
152
0, 0, 0, 0);
153
//Open output URL
154
if (avio_open(&myFormatContext->pb, out_file, AVIO_FLAG_WRITE) < 0) {
155
throw ProcessError(TL("Failed to open output file!"));
156
}
157
158
//Write File Header
159
if (avformat_write_header(myFormatContext, nullptr) < 0) {
160
throw ProcessError(TL("Failed to write file header!"));
161
}
162
myFrameIndex = 0;
163
myPkt = av_packet_alloc();
164
if (myPkt == nullptr) {
165
throw ProcessError(TL("Could not allocate video packet!"));
166
}
167
}
168
169
~GUIVideoEncoder() {
170
int ret = 1;
171
if (!(myCodecCtx->codec->capabilities & AV_CODEC_CAP_DELAY)) {
172
ret = 0;
173
}
174
if (avcodec_send_frame(myCodecCtx, nullptr) < 0) {
175
WRITE_WARNING(TL("Error sending final frame!"));
176
ret = -1;
177
}
178
while (ret >= 0) {
179
ret = avcodec_receive_packet(myCodecCtx, myPkt);
180
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
181
break;
182
} else if (ret < 0) {
183
WRITE_WARNING(TL("Error during final encoding step!"));
184
break;
185
}
186
ret = av_write_frame(myFormatContext, myPkt);
187
av_packet_unref(myPkt);
188
}
189
190
//Write file trailer
191
av_write_trailer(myFormatContext);
192
avio_closep(&myFormatContext->pb);
193
194
//Clean
195
avcodec_free_context(&myCodecCtx);
196
av_frame_free(&myFrame);
197
av_packet_free(&myPkt);
198
avformat_free_context(myFormatContext);
199
}
200
201
void writeFrame(uint8_t* buffer) {
202
if (av_frame_make_writable(myFrame) < 0) {
203
throw ProcessError();
204
}
205
uint8_t* inData[1] = { buffer }; // RGBA32 has one plane
206
int inLinesize[1] = { 4 * myCodecCtx->width }; // RGBA stride
207
sws_scale(mySwsContext, inData, inLinesize, 0, myCodecCtx->height,
208
myFrame->data, myFrame->linesize);
209
myFrame->pts = myFrameIndex;
210
int r = avcodec_send_frame(myCodecCtx, myFrame);
211
if (r < 0) {
212
char errbuf[64];
213
av_strerror(r, errbuf, 64);
214
throw ProcessError(TL("Error sending frame for encoding!"));
215
}
216
int ret = 0;
217
while (ret >= 0) {
218
ret = avcodec_receive_packet(myCodecCtx, myPkt);
219
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
220
break;
221
} else if (ret < 0) {
222
throw ProcessError(TL("Error during encoding!"));
223
}
224
/* rescale output packet timestamp values from codec to stream timebase */
225
av_packet_rescale_ts(myPkt, myCodecCtx->time_base, myFormatContext->streams[0]->time_base);
226
myPkt->stream_index = 0;
227
ret = av_write_frame(myFormatContext, myPkt);
228
av_packet_unref(myPkt);
229
}
230
myFrameIndex++;
231
}
232
233
private:
234
AVFormatContext* myFormatContext;
235
SwsContext* mySwsContext;
236
AVCodecContext* myCodecCtx;
237
AVFrame* myFrame;
238
AVPacket* myPkt;
239
int myFrameIndex;
240
241
};
242
243