Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
GUIVideoEncoder.h
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2001-2024 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/****************************************************************************/
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
42extern "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
59
60
61// ===========================================================================
62// class definitions
63// ===========================================================================
69public:
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
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
233private:
234 AVFormatContext* myFormatContext;
235 SwsContext* mySwsContext;
236 AVCodecContext* myCodecCtx;
237 AVFrame* myFrame;
238 AVPacket* myPkt;
240
241};
#define WRITE_WARNING(msg)
Definition MsgHandler.h:295
#define TL(string)
Definition MsgHandler.h:315
A simple video encoder from RGBA pics to anything ffmpeg can handle.
SwsContext * mySwsContext
AVFormatContext * myFormatContext
void writeFrame(uint8_t *buffer)
GUIVideoEncoder(const char *const out_file, const int width, const int height, double frameDelay)
AVCodecContext * myCodecCtx